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.google.common.collect.Lists;
import com.google.gson.Gson;
import com.qmth.distributed.print.business.bean.dto.*;
import com.qmth.distributed.print.business.bean.dto.approvalForm.*;
import com.qmth.distributed.print.business.bean.dto.examObject.ExamObjectDto;
import com.qmth.distributed.print.business.bean.dto.excel.ExamStudentImportDto;
import com.qmth.distributed.print.business.bean.params.*;
import com.qmth.distributed.print.business.bean.result.WorkResult;
import com.qmth.distributed.print.business.bean.result.examTaskStudent.ExamTaskExamStudentImportResult;
import com.qmth.distributed.print.business.bean.result.examTaskStudent.ExamTaskStudentObjectResult;
import com.qmth.distributed.print.business.bean.result.examTaskStudent.StudentInfo;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.*;
import com.qmth.distributed.print.business.mapper.ExamTaskMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.distributed.print.business.templete.execute.AsyncPaperReviewPdfExportService;
import com.qmth.distributed.print.business.util.PdfUtil;
import com.qmth.distributed.print.business.util.pdf.CreatePdfNewUtil;
import com.qmth.teachcloud.common.bean.dto.AssignTeacherDto;
import com.qmth.teachcloud.common.bean.dto.BlurryUserDto;
import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
import com.qmth.teachcloud.common.bean.dto.excel.DescribeImportDto;
import com.qmth.teachcloud.common.bean.params.BasicStudentExtrasParam;
import com.qmth.teachcloud.common.bean.result.BasicExamStudentResult;
import com.qmth.teachcloud.common.bean.result.SysUserResult;
import com.qmth.teachcloud.common.bean.result.TbTaskDetailResult;
import com.qmth.teachcloud.common.bean.tiku.TikuPaperInfo;
import com.qmth.teachcloud.common.bean.vo.FilePathVo;
import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.*;
import com.qmth.teachcloud.common.enums.*;
import com.qmth.teachcloud.common.enums.log.OperationTypeEnum;
import com.qmth.teachcloud.common.kit.TikuUtils;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.*;
import com.qmth.teachcloud.common.util.excel.ExcelError;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 命题任务 服务实现类
 * </p>
 *
 * @author xf
 * @since 2021-03-23
 */
@Service
public class ExamTaskServiceImpl extends ServiceImpl<ExamTaskMapper, ExamTask> implements ExamTaskService {

    private static final Logger logger = LoggerFactory.getLogger(ExamTaskServiceImpl.class);

    @Resource
    private BasicTeachClazzService basicTeachClazzService;
    @Resource
    private BasicExamStudentService basicExamStudentService;
    @Resource
    private ExamTaskTempService examTaskTempService;
    @Resource
    private ExamTaskReviewLogService examTaskReviewLogService;
    @Resource
    private ExamTaskDetailService examTaskDetailService;
    @Resource
    private BasicAttachmentService basicAttachmentService;
    @Resource
    @Lazy
    private PrintCommonService printCommonService;
    @Resource
    private BasicCourseService basicCourseService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysUserRoleService sysUserRoleService;
    @Resource
    private ExamCardService examCardService;
    @Resource
    private ExamDetailCourseService examDetailCourseService;
    @Resource
    private ExamDetailService examDetailService;
    @Resource
    TBTaskService tbTaskService;
    @Resource
    private TBTaskPdfService tbTaskPdfService;
    @Resource
    private ExamTaskPaperLogService examTaskPaperLogService;
    @Resource
    AsyncPaperReviewPdfExportService asyncPaperReviewPdfExportService;
    @Resource
    TeachcloudCommonService teachcloudCommonService;
    @Resource
    ActivitiService activitiService;
    @Resource
    TFFlowApproveService tfFlowApproveService;
    @Resource
    TaskService taskService;
    @Resource
    BasicCardRuleService basicCardRuleService;
    @Resource
    ExamPrintPlanService examPrintPlanService;
    @Resource
    RedisUtil redisUtil;
    @Resource
    SysOrgService sysOrgService;
    @Resource
    FileStoreUtil fileStoreUtil;
    @Resource
    ExamTaskService examTaskService;
    @Resource
    ExamTaskPaperDataService examTaskPaperDataService;
    @Resource
    ExamStudentService examStudentService;
    @Resource
    TFFlowJoinService tfFlowJoinService;
    @Resource
    CreatePdfNewUtil createPdfNewUtil;
    @Resource
    BasicPrintConfigService basicPrintConfigService;
    @Resource
    TFFlowLogService tfFlowLogService;
    @Resource
    CommonCacheService commonCacheService;
    @Resource
    TFCustomFlowService tfCustomFlowService;
    @Resource
    BasicExamService basicExamService;
    @Resource
    BasicSemesterService basicSemesterService;
    @Resource
    BasicStudentService basicStudentService;
    @Resource
    BasicClazzService basicClazzService;
    @Resource
    private BasicRoleDataPermissionService basicRoleDataPermissionService;
    @Resource
    private BasicOperationLogService basicOperationLogService;
    @Resource
    private TeachCourseService teachCourseService;
    @Resource
    private TeachClazzService teachClazzService;
    @Resource
    private TeachStudentService teachStudentService;
    @Resource
    private RedisCounterUtil redisCounterUtil;
    @Resource
    private FileUploadService fileUploadService;
    @Resource
    private TikuUtils tikuUtils;

    @Override
    public IPage<ExamTaskDto> list(Long semesterId, Long examId, Boolean enable, String status, Long cardRuleId, String courseCode, String paperNumber, Long startTime, Long endTime, String userName, String createName, Integer pageNumber, Integer pageSize) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        userName = SystemConstant.translateSpecificSign(userName);
        createName = SystemConstant.translateSpecificSign(createName);
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        Page<ExamTaskDto> page = new Page<>(pageNumber, pageSize);
        IPage<ExamTaskDto> examTaskDtoIPage = this.baseMapper.listPage(page, schoolId, semesterId, examId, enable, status, cardRuleId, courseCode, paperNumber, startTime, endTime, dpr, userName, createName);
        for (ExamTaskDto record : examTaskDtoIPage.getRecords()) {
            // 判断审批表状态
            String cellPaperNumber = record.getPaperNumber();
            List<PrintPlanStatusEnum> printPlanStatusEnumList = new ArrayList<>();
            printPlanStatusEnumList.add(PrintPlanStatusEnum.PRINTING);
            printPlanStatusEnumList.add(PrintPlanStatusEnum.FINISH);
            List<ExamPackageDetail> examPackageDetailDatasource = this.baseMapper.findExamPackageDetailByPaperNumber(schoolId, examId, record.getCourseCode(), cellPaperNumber, printPlanStatusEnumList);
            record.setApproveFormStatus(examPackageDetailDatasource.size() > 0);
        }
        return examTaskDtoIPage;
    }

    @Override
    public List<BlurryUserDto> listUser(String param) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        return this.baseMapper.listUser(schoolId, SystemConstant.translateSpecificSign(param), orgIds);
    }

    @Transactional
    @Override
    public ExamTask assignUser(ExamTask examTask) {
        ExamStatusEnum examStatusEnum = ExamStatusEnum.DRAFT;
        Long flowId = null;
        UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
        ExamTask task = this.getById(examTask.getId());
        // 指派命题老师
        if (task.getStatus().name().equals(ExamStatusEnum.DRAFT.name())) {
            if (!task.getEnable()) {
                throw ExceptionResultEnum.ERROR.exception("命题任务已禁用，不能指派命题老师");
            }

            if (Objects.nonNull(examTask.getUserId())) {
                examStatusEnum = ExamStatusEnum.STAGE;
                Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                        examTask,
                        TFCustomTypeEnum.ELECTRON_FLOW,
                        examTask.getUserId(),
                        true);
                map = activitiService.customFlowStart(map);
                flowId = SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID));
                map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.NEW);
                tfFlowJoinService.saveOrUpdate(map);
                examTask.setFlowId(flowId);
            }
        }
        // 更改命题老师
        else if (task.getStatus().name().equals(ExamStatusEnum.STAGE.name())) {
            if (task.getEnable()) {
                throw ExceptionResultEnum.ERROR.exception("命题任务禁用后，才能更改命题老师");
            }

            if (examTask.getUserId() == null) {
                examStatusEnum = ExamStatusEnum.DRAFT;
            } else {
                examStatusEnum = ExamStatusEnum.STAGE;

                if (examTask.getUserId().longValue() != task.getUserId().longValue()) {
                    Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                            examTask,
                            TFCustomTypeEnum.ELECTRON_FLOW,
                            examTask.getUserId(),
                            true);
                    map = activitiService.customFlowStart(map);
                    flowId = SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID));
                    map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.NEW);

                    tfFlowJoinService.deleteByObjectIdAndFlowId(examTask.getId(), null);
                    tfFlowJoinService.saveOrUpdate(map);
                    examTask.setFlowId(flowId);
                } else {
                    flowId = task.getFlowId();
                }
            }
        }

        updateWrapper.lambda().set(ExamTask::getUserId, examTask.getUserId())
                .set(ExamTask::getStatus, examStatusEnum)
                .set(ExamTask::getFlowId, flowId)
                .eq(ExamTask::getId, examTask.getId());
        this.update(updateWrapper);
        return examTask;
    }

    @Transactional
    @Override
    public boolean enable(ExamTask examTask) {
        if (examTask.getId() == null) {
            throw ExceptionResultEnum.ERROR.exception("参数有误");
        }

        ExamTask task = this.getById(examTask.getId());
        if (Objects.nonNull(task.getFlowId())) {
            TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(task.getFlowId());
            if (tfFlowApprove.getStatus().equals(FlowStatusEnum.FINISH) ||
                    tfFlowApprove.getStatus().equals(FlowStatusEnum.END)) {
                throw ExceptionResultEnum.ERROR.exception("流程状态为已结束或已终止，不能启用/禁用操作");
            }
        }

        UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamTask::getEnable, examTask.getEnable()).eq(ExamTask::getId, task.getId());
        // 启用时，后续状态清空
        if (!examTask.getEnable()) {//解除题卡绑定
            examTaskDetailService.resetCardId(examTask.getId());
        }
        return this.update(updateWrapper);
    }

    /**
     * 保存命题任务新
     *
     * @param examTask
     * @return
     */
    @Override
    @Transactional
    public ExamTask saveExamTaskNew(ExamTask examTask) {
        try {
            Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
            Long examId = examTask.getExamId();
            String courseCode = examTask.getCourseCode();
            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(examId);
            examTask.setSchoolId(schoolId);
            examTask.setCardRuleId(basicPrintConfig.getCardRuleId());
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            if (StringUtils.isNotBlank(examTask.getPaperNumber())) {
                QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getExamId, examId).eq(ExamTask::getPaperNumber, examTask.getPaperNumber());
                ExamTask task = this.getOne(taskQueryWrapper);
                if (task != null) {
                    BasicExam basicExam = basicExamService.getById(examId);
                    throw ExceptionResultEnum.ERROR.exception("试卷编号[" + examTask.getPaperNumber() + "]在考试[" + basicExam.getName() + "]下已存在");
                }
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                String paperNumber = printCommonService.createPaperNumber(schoolId, examId);
                examTask.setPaperNumber(paperNumber);
            }

            String sequence = printCommonService.createCourseSequence(schoolId, examTask.getCourseCode());
            examTask.setSequence(sequence);

            examTask.insertInfo(sysUser.getId());
            examTask.updateInfo(sysUser.getId());

            examTask.setReview(basicPrintConfig.getReview());
            // 取课程所在机构
            Long teachingRoomId = basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, courseCode);
            examTask.setOrgId(teachingRoomId);
            examTask.setTeachingRoomId(teachingRoomId);
            if (examTask.getUserId() == null) {
                examTask.setStatus(ExamStatusEnum.DRAFT);
            } else if (Objects.nonNull(examTask.getReview()) && examTask.getReview()) {
                examTask.setStatus(ExamStatusEnum.STAGE);
                Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                        examTask,
                        TFCustomTypeEnum.ELECTRON_FLOW,
                        examTask.getUserId(),
                        true);
                map = activitiService.customFlowStart(map);
                examTask.setFlowId(SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID)));
                map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.NEW);
                tfFlowJoinService.saveOrUpdate(map);
            } else if (Objects.nonNull(examTask.getReview()) && !examTask.getReview()) {
                examTask.setStatus(ExamStatusEnum.STAGE);
            }
            this.save(examTask);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ActivitiObjectNotFoundException) {
                ResultUtil.error("流程不存在");
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return examTask;
    }

    @Override
    public ExamTaskImportResultDto importFile(MultipartFile file) throws IOException, NoSuchFieldException {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamTaskImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            // 课程代码与试卷有相同数据map
            Map<String, String> courseCodePaperNumberMap = new HashMap<>();
            // 试卷编号重复set
            Set<String> paperNubmerSet = new HashSet<>();
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            // 只允许导入一个sheet
            if (finalExcelList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
            }
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> examTaskTempList = excelMap.get(i);
                for (int y = 0; y < examTaskTempList.size(); y++) {
                    ExamTaskImportDto userImportDto = (ExamTaskImportDto) examTaskTempList.get(y);

                    String excelCourseCode = userImportDto.getCourseCode();
                    String excelCourseName = userImportDto.getCourseName();
                    String excelPaperNumber = userImportDto.getPaperNumber();
                    String excelTeacherAccount = userImportDto.getUserAccount();
                    String excelTeacherName = userImportDto.getUserName();

                    // 课程是否存在并且与名称对应
                    QueryWrapper<BasicCourse> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda().eq(BasicCourse::getSchoolId, schoolId).eq(BasicCourse::getCode, excelCourseCode);
                    BasicCourse course = basicCourseService.getOne(queryWrapper);
                    // 校验课程名称和课程代码
                    if (course == null) {
                        excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行课程代码[" + excelCourseCode + "]在系统中不存在"));
                    } else if (!course.getName().equals(excelCourseName)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行课程代码[" + excelCourseCode + "]对应的课程名称与课程管理中不一致。文件中为[" + excelCourseName + "]，课程管理为[" + course.getName() + "]"));
                    }
                    if (courseCodePaperNumberMap.containsKey(excelCourseCode)) {
                        String paperNumber = courseCodePaperNumberMap.get(excelCourseCode);
                        if (paperNumber != null && paperNumber.equals(excelPaperNumber)) {
                            excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行课程代码[" + excelCourseCode + "]与试卷编号[" + excelPaperNumber + "]有相同数据"));
                        }
                    } else {
                        courseCodePaperNumberMap.put(excelCourseCode, excelPaperNumber);
                    }

                    // 校验试卷编号重复
                    if (StringUtils.isNotBlank(excelPaperNumber)) {
                        if (paperNubmerSet.contains(excelPaperNumber)) {
                            excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "试卷编号[" + excelPaperNumber + "]有相同数据"));
                        } else {
                            paperNubmerSet.add(excelPaperNumber);
                        }
                    }

                    // 校验命题老师
                    AssignTeacherDto assignTeacherDto = sysUserService.listUser(excelCourseCode, null);
                    List<SysUserResult> listUser = Objects.nonNull(assignTeacherDto) ? assignTeacherDto.getAllUserList() : new ArrayList<>();
                    Map<String, String> userMap = listUser.stream().collect(Collectors.toMap(SysUserResult::getLoginName, SysUserResult::getRealName));
                    if (userMap == null) {
                        excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行课程代码[" + excelCourseCode + "]所属机构下未找到命题老师"));
                    } else {
                        List<SysUserResult> courseUserList = Objects.nonNull(assignTeacherDto) ? assignTeacherDto.getCourseUserList() : new ArrayList<>();
                        Map<String, String> courseUserMap = courseUserList.stream().collect(Collectors.toMap(SysUserResult::getLoginName, SysUserResult::getRealName));
                        if (!courseUserMap.isEmpty()) {
                            userMap.putAll(courseUserMap);
                        }
                        if (StringUtils.isNotBlank(excelTeacherAccount)) {
                            if (StringUtils.isBlank(excelTeacherName)) {
                                excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行命题老师姓名不能为空"));
                            } else {
                                if (StringUtils.isBlank(userMap.get(excelTeacherAccount))) {
                                    excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行命题老师账号[" + excelTeacherAccount + "]系统中不存在"));
                                } else {
                                    String teacherName = userMap.get(excelTeacherAccount);
                                    if (!excelTeacherName.equals(teacherName)) {
                                        excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行命题老师姓名与系统不一致。文件中为[" + excelTeacherName + "]，用户管理中为[" + teacherName + "]"));
                                    }
                                }
                            }
                        } else {
                            if (StringUtils.isNotBlank(excelTeacherName)) {
                                excelErrorTemp.add(new ExcelError(y + 1, "第" + (y + 1) + "行命题老师账号必填"));
                            }
                        }
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                List<String> errors = excelErrorTemp.stream().map(m -> m.getExcelErrorType()).collect(Collectors.toList());
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
            }
            return finalExcelList;
        }, 2);
        return executeImportExamTaskBatch(finalList);
    }

    @Transactional
    ExamTaskImportResultDto executeImportExamTaskBatch(List<LinkedMultiValueMap<Integer, Object>> finalList) {
        // 学校ID
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        // 用户
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        // 批次号
        Gson gson = new Gson();
        String batchNo = String.valueOf(System.nanoTime());
        ExamTaskImportResultDto examTaskImportResultDto = new ExamTaskImportResultDto();
        examTaskImportResultDto.setBatchNo(batchNo);
        List<ExamTaskDto> tasks = new ArrayList<>();

        List<BasicCourse> userCourses = printCommonService.list(null, null, null);
        // 错误信息
        StringJoiner stringJoiner = new StringJoiner(";");
        for (int i = 0; i < finalList.size(); i++) {
            LinkedMultiValueMap<Integer, Object> excelMap = finalList.get(i);
            List<Object> examTaskTempList = excelMap.get(i);
            for (int y = 0; y < examTaskTempList.size(); y++) {
                ExamTaskImportDto examTaskImportDto = (ExamTaskImportDto) examTaskTempList.get(y);
                ExamTaskTemp examTaskTemp = gson.fromJson(gson.toJson(examTaskImportDto), ExamTaskTemp.class);
                // 校验该用户是否有科目
                long matchCourseCount = userCourses.stream().filter(m -> examTaskTemp.getCourseCode().equals(m.getCode())).count();
                if (matchCourseCount == 0) {
                    throw ExceptionResultEnum.ERROR.exception("用户没有科目[" + examTaskTemp.getCourseName() + "（" + examTaskTemp.getCourseCode() + "）]权限");
                }
                examTaskTemp.setId(SystemConstant.getDbUuid());
                examTaskTemp.setBatchNo(batchNo);
                examTaskTemp.setSchoolId(schoolId);
                examTaskTemp.setCreateId(sysUser.getId());
                examTaskTemp.setCreateTime(System.currentTimeMillis());
                //保存
                examTaskTempService.save(examTaskTemp);

                ExamTaskDto examTaskDto = new ExamTaskDto();
                examTaskDto.setCourseCode(examTaskTemp.getCourseCode());
                examTaskDto.setCourseName(examTaskTemp.getCourseName());
                examTaskDto.setPaperNumber(examTaskTemp.getPaperNumber());

                QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(SysUser::getSchoolId, schoolId).eq(SysUser::getLoginName, examTaskTemp.getUserAccount());
                SysUser sysUser1 = sysUserService.getOne(queryWrapper);
                AssignTeacherDto assignTeacherDto;
                if (sysUser1 != null) {
                    assignTeacherDto = listUsers(examTaskTemp.getCourseCode(), String.valueOf(sysUser1.getId()));
                } else {
                    assignTeacherDto = listUsers(examTaskTemp.getCourseCode(), null);
                }
                examTaskDto.setUsers(Objects.nonNull(assignTeacherDto) ? assignTeacherDto.getAllUserList() : new ArrayList<>());
                String userName = examTaskImportDto.getUserName();
                String userAccount = examTaskImportDto.getUserAccount();
                if (SystemConstant.strNotNull(userName) && SystemConstant.strNotNull(userAccount)) {
                    List<SysUserResult> match = assignTeacherDto.getAllUserList().stream()
                            .filter(e -> e.getLoginName().equals(userAccount) && e.getRealName().equals(userName))
                            .collect(Collectors.toList());
                    if (match.size() > 1) {
                        throw ExceptionResultEnum.ERROR.exception("数据异常");
                    }
                    if (match.size() == 1) {
                        SysUserResult tmp = match.get(0);
                        examTaskDto.setUserId(String.valueOf(tmp.getId()));
                        examTaskDto.setUserName(tmp.getRealName());
                    }

                    if (Objects.nonNull(assignTeacherDto) && !CollectionUtils.isEmpty(assignTeacherDto.getCourseUserList())) {
                        List<SysUserResult> matchCouserUser = assignTeacherDto.getCourseUserList().stream()
                                .filter(e -> e.getLoginName().equals(userAccount) && e.getRealName().equals(userName))
                                .collect(Collectors.toList());
                        if (matchCouserUser.size() > 1) {
                            throw ExceptionResultEnum.ERROR.exception("数据异常");
                        }
                        if (matchCouserUser.size() == 1) {
                            SysUserResult tmp = matchCouserUser.get(0);
                            examTaskDto.setUserId(String.valueOf(tmp.getId()));
                            examTaskDto.setUserName(tmp.getRealName());
                        }
                    }
                }
                tasks.add(examTaskDto);
            }
        }

        // 异常抛出 by 20230214
        String errorMsg = stringJoiner.toString();
        if (errorMsg.length() > 0) {
            throw ExceptionResultEnum.ERROR.exception(errorMsg);
        }

        // 设置重复信息，页面做二次校验
        examTaskImportResultDto.setErrorMsg(stringJoiner.toString());
        examTaskImportResultDto.setTasks(tasks);
        return examTaskImportResultDto;
    }

    private AssignTeacherDto listUsers(String courseCode, String userId) {
        return sysUserService.listUser(courseCode, userId);
    }

    @Override
    public List<ExamTask> saveBatch(ExamTask task) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        Long examId = task.getExamId();
        BasicExam basicExam = basicExamService.getById(examId);

        task.setSchoolId(schoolId);
        if (StringUtils.isBlank(task.getBatchNo())) {
            throw ExceptionResultEnum.ERROR.exception("导入批次号不能为空，请重新导入");
        }

        List<ExamTask> list = new ArrayList<>();
        List<Map<String, String>> userList = task.getUsers();

        StringJoiner stringJoiner = new StringJoiner(",");
        for (Map<String, String> userMap : userList) {
            String courseCode = userMap.get("courseCode");
            // 配置信息
            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(examId);
            Long cardRuleId = basicPrintConfig.getCardRuleId();

            ExamTask examTask = new ExamTask();
            examTask.setSchoolId(task.getSchoolId());
            examTask.setOrgId(basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, courseCode));
            examTask.setCourseCode(userMap.get("courseCode"));
            examTask.setCourseName(userMap.get("courseName"));
            examTask.setExamId(examId);
            examTask.setCardRuleId(cardRuleId);

            String sequence = printCommonService.createCourseSequence(schoolId, examTask.getCourseCode());
            examTask.setSequence(sequence);

            String paperNumber = userMap.get("paperNumber");
            if (StringUtils.isNotBlank(paperNumber)) {
                QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getExamId, examId).eq(ExamTask::getPaperNumber, paperNumber);
                ExamTask task1 = this.getOne(taskQueryWrapper);
                if (task1 != null) {
                    stringJoiner.add(String.format("试卷编号[%s]在考试[%s]下已使用", paperNumber, basicExam.getName()));
//                    // 试卷编号已存在，直接跳过
                    continue;
                }
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                paperNumber = printCommonService.createPaperNumber(schoolId, examId);
            }
            examTask.setPaperNumber(paperNumber);
            examTask.setStartTime(task.getStartTime());
            examTask.setEndTime(task.getEndTime());
            examTask.setEnable(true);
            examTask.setBatchNo(task.getBatchNo());
            examTask.insertInfo(sysUser.getId());

            // 取课程所在机构
            Long teachingRoomId = basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, examTask.getCourseCode());
            examTask.setOrgId(teachingRoomId);
            examTask.setTeachingRoomId(teachingRoomId);

            String userId = userMap.get("userId");
            if (userId != null && userId.length() > 0 && !userId.equals("null")) {
                examTask.setUserId(SystemConstant.convertIdToLong(userId));
                examTask.setStatus(ExamStatusEnum.STAGE);
            } else {
                examTask.setStatus(ExamStatusEnum.DRAFT);
            }
            examTask.setReview(basicPrintConfig.getReview());
            list.add(examTask);
        }

        // 有异常，抛出
        if (stringJoiner.toString().length() > 0) {
            throw ExceptionResultEnum.ERROR.exception(stringJoiner.toString());
        }
        this.saveBatch(list);
        for (ExamTask examTask : list) {
            if (Objects.nonNull(examTask.getReview()) && examTask.getReview() && Objects.nonNull(examTask.getUserId()) && Objects.isNull(examTask.getFlowId())) {
                Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                        examTask,
                        TFCustomTypeEnum.ELECTRON_FLOW,
                        examTask.getUserId(),
                        true);
                map = activitiService.customFlowStart(map);
                examTask.setFlowId(SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID)));
                map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.NEW);
                tfFlowJoinService.saveOrUpdate(map);
                examTask.updateInfo(sysUser.getId());
                this.updateById(examTask);
                activitiService.sendFlowStartMsg(examTask.getFlowId());
            }
        }
        return list;
    }

    @Override
    public IPage<ExamTaskDto> listTaskApply(Long semesterId, Long examId, String auditStatus, Long cardRuleId, String
            courseCode, String paperNumber, Long startTime, Long endTime, String userName, Integer pageNumber, Integer pageSize) {
        userName = SystemConstant.translateSpecificSign(userName);
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        IPage<ExamTaskDto> examTaskDtoIPage = this.baseMapper.listTaskApply(new Page<>(pageNumber, pageSize), schoolId, semesterId, examId, auditStatus, cardRuleId, courseCode, paperNumber, startTime, endTime, sysUser.getId(), userName);
        for (ExamTaskDto record : examTaskDtoIPage.getRecords()) {
            if ((Objects.nonNull(record.getReview()) && record.getReview() && FlowStatusEnum.FINISH.name().equals(record.getAuditStatus()) && !ExamModelEnum.MODEL3.equals(record.getExamModel()))) {
                record.setApproveFormStatus(true);
            } else {
                record.setApproveFormStatus(false);
            }
        }
        return examTaskDtoIPage;
    }

    @Override
    public IPage<ExamTaskDto> listTaskReviewUnaudited(Long semesterId, Long examId, String courseCode, String paperNumber, Long userId, Long
            cardRuleId, Long startTime, Long endTime, Long startCreateTime, Long endCreateTime, String createName, Integer pageNumber, Integer pageSize) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        createName = SystemConstant.translateSpecificSign(createName);
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        return this.baseMapper.listTaskReviewUnaudited(new Page<>(pageNumber, pageSize), schoolId, semesterId, examId, courseCode, paperNumber, userId, cardRuleId, startTime, endTime, startCreateTime, endCreateTime, createName, sysUser.getId());
    }

    @Override
    public IPage<ExamTaskDto> listTaskReviewAudited(Long semesterId, Long examId, String reviewStatus, String courseCode, String
            paperNumber, Long userId, Long cardRuleId, Long startTime, Long endTime, Long startCreateTime, Long endCreateTime, String createName, Integer pageNumber, Integer pageSize) {
        createName = SystemConstant.translateSpecificSign(createName);
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        return this.baseMapper.listTaskReviewAudited(new Page<>(pageNumber, pageSize), schoolId, semesterId, examId, reviewStatus, courseCode, paperNumber, userId, cardRuleId, startTime, endTime, orgIds, startCreateTime, endCreateTime, createName, sysUser.getId());
    }

    @Transactional
    @Override
    public boolean taskReviewSave(ExamTaskReviewLog taskReviewLog) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        // 校验状态，可能被撤回
        ExamTask examTask = this.getById(taskReviewLog.getExamTaskId());

        taskReviewLog.setOperateId(sysUser.getId());
        taskReviewLog.setOperateTime(System.currentTimeMillis());
        // 审核日志
        examTaskReviewLogService.save(taskReviewLog);

        ExamTaskPaperLog examTaskPaperLog = examTaskPaperLogService.getByExamTaskIdAndReview(taskReviewLog.getExamTaskId(), false);
        if (examTaskPaperLog == null) {
            if (!ExamStatusEnum.SUBMIT.equals(examTask.getStatus())) {
                throw ExceptionResultEnum.ERROR.exception("当前状态不能审核");
            }
        }
        return true;
    }

    @Override
    public IPage<ExamTaskDetailDto> listTaskPaper(Long semesterId, Long examId, String courseCode, String paperNumber, Long startTime, Long endTime, MakeMethodEnum makeMethod, Long cardRuleId, Integer pageNumber, Integer pageSize) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        // 查询用户角色是否包含命题老师
//        List<SysRole> list = sysUserRoleService.listRoleByUserId(sysUser.getId());
//        boolean containsQuestionTeacher = list.stream().anyMatch(m -> RoleTypeEnum.QUESTION_TEACHER.equals(m.getType()));
        boolean containsQuestionTeacher = false;
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        Page<ExamTaskDetailDto> page = new Page<>(pageNumber, pageSize);
        return this.baseMapper.listTaskPaper(page, schoolId, semesterId, examId, courseCode, paperNumber, startTime, endTime, dpr, containsQuestionTeacher, sysUser.getId(), makeMethod, cardRuleId);
    }

    @Override
    public List<RelatePaperDto> listPaperTypes(Long examTaskId, Long printPlanId, String courseCode) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        List<RelatePaperDto> list = this.baseMapper.listPaperNumbers(schoolId, courseCode, ExamStatusEnum.SUBMIT.name(), dpr);
        for (RelatePaperDto relatePaperDto : list) {
            QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, relatePaperDto.getId()).eq(ExamTaskDetail::getEnable, true);
            ExamTaskDetail examTaskDetail = this.examTaskDetailService.getOne(queryWrapper);
            if (examTaskDetail != null) {
                // 所有卷型
                String paperType = examTaskDetail.getPaperType();
                relatePaperDto.setPaperTypes(Arrays.asList(paperType.split(",")));
            }
        }
        return list;
    }

    @Transactional
    @Override
    public Map<String, Object> saveExamTaskDetail(ExamTaskDetail examTaskDetail) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        Optional.ofNullable(examTaskDetail.getExamTaskId()).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("命题任务ID不能为空"));
        if (StringUtils.isBlank(examTaskDetail.getOperateType())) {
            throw ExceptionResultEnum.ERROR.exception("命题任务提交类型不能为空");
        }

        // 更新examTask状态status
        ExamTask examTask = this.getById(examTaskDetail.getExamTaskId());
        // 提交时，校验
        if (ExamStatusEnum.SUBMIT.name().equals(examTaskDetail.getOperateType())) {
            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(examTask.getExamId());
            if (basicPrintConfig == null) {
                throw ExceptionResultEnum.ERROR.exception("印品配置未设置");
            }
            validSubmitParam(examTaskDetail, basicPrintConfig.getPrintContent());
        }

        Map<String, Object> map = new HashMap<>();
        if (ExamStatusEnum.SUBMIT.name().equals(examTaskDetail.getOperateType()) && examTask.getReview()) {
            // 审核一级
            Task task = taskService.createTaskQuery().processInstanceId(String.valueOf(examTask.getFlowId())).singleResult();
            if (Objects.nonNull(task)) {
                map.computeIfAbsent(SystemConstant.FLOW_TASK_ID, v -> task.getId());
                map.computeIfAbsent(SystemConstant.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
                map.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
                map.computeIfAbsent(SystemConstant.APPROVE_USER_IDS, v -> examTaskDetail.getApproveUserIds());
                map = activitiService.taskApprove(map);
                map.put(SystemConstant.SEND_FLOW_MQ, true);
            }
        }

        UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .set(ExamTask::getStatus, examTaskDetail.getOperateType())
                .set(ExamTask::getUpdateId, sysUser.getId())
                .set(ExamTask::getUpdateTime, System.currentTimeMillis())
                .set(ExamTask::getReviewStatus, null)
                .set(ExamTask::getFlowId, examTask.getFlowId())
                .eq(ExamTask::getId, examTask.getId());
        this.update(updateWrapper);

        ExamTaskDetail detail = examTaskDetailService.getByExamTaskIdNotValid(examTaskDetail.getExamTaskId());
        if (detail != null) {
            examTaskDetail.setId(detail.getId());
            // 已曝光试卷和未曝光试卷赋值
            examTaskDetail.setExposedPaperType(detail.getExposedPaperType());
            if (StringUtils.isNotBlank(detail.getExposedPaperType())) {
                List<String> paperTypeAll = new ArrayList<>(Arrays.asList(examTaskDetail.getPaperType().split(",")));
                List<String> exposePaperTypes = Arrays.asList(detail.getExposedPaperType().split(","));
                for (String exposePaperType : exposePaperTypes) {
                    if (paperTypeAll.contains(exposePaperType)) {
                        paperTypeAll.remove(exposePaperType);
                    }
                }
                examTaskDetail.setUnexposedPaperType(String.join(",", paperTypeAll));
            } else {
                examTaskDetail.setUnexposedPaperType(examTaskDetail.getPaperType());
            }
        } else {
            // 已曝光试卷和未曝光试卷赋值（新增时，已曝光试卷为null，未曝光试卷为paper_type）
            examTaskDetail.setUnexposedPaperType(examTaskDetail.getPaperType());
        }
        examTaskDetail.setUpdateId(sysUser.getId());
        examTaskDetail.setUpdateTime(System.currentTimeMillis());
        examTaskDetailService.saveOrUpdate(examTaskDetail);

        // 将题卡更新为已绑定
        List<PaperInfoVo> paperAttachmentIdsJson = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
        List<String> cardIds = paperAttachmentIdsJson.stream().filter(m -> CardTypeEnum.CUSTOM.name().equals(m.getCardType())).map(m -> m.getCardId()).collect(Collectors.toList());
        for (String cardId : cardIds) {
            UpdateWrapper<ExamCard> cardUpdateWrapper = new UpdateWrapper<>();
            cardUpdateWrapper.lambda().set(ExamCard::getUsed, true).eq(ExamCard::getId, cardId);
            examCardService.update(cardUpdateWrapper);
        }
        return map;
    }

    @Override
    @Transactional
    public Map<String, Object> status(ExamTask examTask) {
        Optional.ofNullable(examTask.getId()).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("参数有误"));
        ExamTask task = this.getById(examTask.getId());
        if (Objects.nonNull(task.getReview()) && !task.getReview()) {
            throw ExceptionResultEnum.ERROR.exception("命题任务不需要审核，不能撤回");
        }
        Optional.ofNullable(task.getFlowId()).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("审核流程有误，不能撤回"));

        TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(task.getFlowId());
        Optional.ofNullable(tfFlowApprove).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("审核流程进度丢失，不能撤回"));

        if (tfFlowApprove.getSetup().intValue() != 2) {
            throw ExceptionResultEnum.ERROR.exception("当前审核节点不能撤回");
        }

        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        SysOrg sysOrg = commonCacheService.orgCache(sysUser.getOrgId());
        List<Task> flowTaskList = taskService.createTaskQuery().processInstanceId(String.valueOf(task.getFlowId())).list();
        Map<String, Object> map = new HashMap<>();
        map.computeIfAbsent(SystemConstant.FLOW_TASK_ID, v -> flowTaskList.get(0).getId());
        map.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.CANCEL);
        String remark = Objects.nonNull(sysOrg) ? sysOrg.getName() : "无机构";
        map.computeIfAbsent(SystemConstant.APPROVE_REMARK, v -> sysUser.getRealName() + "(" + remark + ")" + FlowApprovePassEnum.CANCEL.getTitle());
        map.computeIfAbsent(SystemConstant.APPROVE_SETUP, v -> FlowApproveSetupEnum.SUBMIT.getSetup());
        activitiService.taskApprove(map);

        // 清除cardId
//        ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(examTask.getId());
//        String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
//        if (StringUtils.isNotBlank(paperAttachmentIds)) {
//            List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(paperAttachmentIds);
//            List<String> cardIds = new ArrayList<>();
//            for (PaperInfoVo paperInfoVo : paperInfoVoList) {
//                cardIds.add(paperInfoVo.getCardId());
//                paperInfoVo.setCardId(null);
//                paperInfoVo.setCardType(null);
//                paperInfoVo.setCardTitle(null);
//            }
//            examTaskDetail.setPaperAttachmentIds(JSON.toJSONString(paperInfoVoList));
//            examTaskDetailService.updateById(examTaskDetail);
//
//            // 更新题卡为未使用
//            UpdateWrapper<ExamCard> updateWrapper = new UpdateWrapper<>();
//            updateWrapper.lambda().set(ExamCard::getUsed, false).in(ExamCard::getId, cardIds);
//            examCardService.update(updateWrapper);
//        }
        return map;
    }

    @Override
    public ExamTaskDetailCardDto applyGetOne(Long examTaskId, String source) {
        ExamTaskDetailCardDto detailCardDto = this.baseMapper.applyGetOne(examTaskId, source);
        if (detailCardDto != null) {
            ExamTask examTask = examTaskService.getById(examTaskId);
            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(examTask.getExamId());
            if (basicPrintConfig != null) {
                detailCardDto.setPrintContent(basicPrintConfig.getPrintContent());
                detailCardDto.setReview(basicPrintConfig.getReview());
                detailCardDto.setAuditContent(basicPrintConfig.getAuditContent());
            }
        }
        return detailCardDto;
    }

    @Override
    public void paperDownload(HttpServletResponse response, Long examTaskId) {
        ExamTaskDetailPdfDownloadDto examTaskDetailPdfDownloadDto = examTaskDetailService.findPdfDownload(examTaskId);
        Optional.ofNullable(examTaskDetailPdfDownloadDto).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("数据异常"));

        String rootPath = null;
        try {
            // 本地保存目录
            File tempFile = SystemConstant.getFileTempParentDirVar(SystemConstant.TEMP_PREFIX);
            rootPath = tempFile.getPath() + File.separator + SystemConstant.getNanoId();

            // 下载文件保存目录（用uuid命名）
            String downloadFilePath = rootPath + File.separator + SystemConstant.getNanoId();
            File downloadPathFile = new File(downloadFilePath);
            if (!downloadPathFile.exists()) {
                downloadPathFile.mkdirs();
            }

            StringJoiner dirPath = new StringJoiner("");
            dirPath = dirPath.add(downloadFilePath).add(File.separator)
                    .add(examTaskDetailPdfDownloadDto.getSemesterName()).add(File.separator)
                    .add(examTaskDetailPdfDownloadDto.getExamName()).add(File.separator)
                    .add(examTaskDetailPdfDownloadDto.getCourseNameCode().replaceAll(" ", "")).add(File.separator)
                    .add(examTaskDetailPdfDownloadDto.getPaperNumber()).add(File.separator);
            List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetailPdfDownloadDto.getPaperAttachmentIds());
            // 试卷
            for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                Long attachmentId = Long.valueOf(paperInfoVo.getAttachmentId());
                String name = paperInfoVo.getName();
                if (Objects.nonNull(attachmentId)) {
                    BasicAttachment attachment = basicAttachmentService.getById(attachmentId);
                    Optional.ofNullable(attachment).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("附件数据异常"));
                    String fileName = dirPath + "试卷" + SystemConstant.HYPHEN + name  + attachment.getType();
                    fileUploadService.downloadFile(attachment, fileName);
                }

                // 题卡
                Long cardId = Long.valueOf(paperInfoVo.getCardId());
                if (cardId != null) {
                    ExamCard examCard = examCardService.getById(cardId);

                    String cardHtmlPath = dirPath + "题卡" + SystemConstant.HYPHEN + name + SystemConstant.HTML_PREFIX;
                    String cardPdfPath = dirPath + "题卡" + SystemConstant.HYPHEN + name + SystemConstant.PDF_PREFIX;

                    String htmlContent = createPdfNewUtil.replaceBlankHtmlContent(examCard.getHtmlContent());
                    // html
                    File htmlFile = new File(cardHtmlPath);
                    if (!htmlFile.exists()) {
                        htmlFile.getParentFile().mkdirs();
                        htmlFile.createNewFile();
                    }
                    // 生成html文件
                    FileUtil.writeContent(htmlFile, htmlContent);
                    // 转pdf文件
                    File pdfFile = new File(cardPdfPath);
                    if (!pdfFile.exists()) {
                        pdfFile.getParentFile().mkdirs();
                        pdfFile.createNewFile();
                    }
                    HtmlToPdfUtil.convert(cardHtmlPath, cardPdfPath, PageSizeEnum.A3);
                }
            }
            String zipFileName = downloadPathFile.getName() + SystemConstant.ZIP_PREFIX;
            FileUtil.downloadEncryptZip(response, downloadPathFile, zipFileName);
        } catch (Exception e) {
            throw ExceptionResultEnum.ERROR.exception("文件下载失败");
        } finally {
            if (Objects.nonNull(rootPath)) {
                FileUtil.deleteDirectory(rootPath);
            }
        }
    }

    @Override
    public void checkDataByExamination(Long printPlanId, List<Long> examDetailIdList, SysUser user) {
        Set<String> stringSet = new HashSet<>();
        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);
        List<ExamDetail> examDetailList = new ArrayList<>();
        for (Long examDetailId : examDetailIdList) {
            ExamDetail examDetail = examDetailService.getById(examDetailId);
            if (Objects.isNull(examDetail)) {
                stringSet.add(String.format("印刷计划[%s]没有考场ID[%s]的数据", examPrintPlan.getName(), examDetailId));
            }
            List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.list(new QueryWrapper<ExamDetailCourse>().lambda()
                    .eq(ExamDetailCourse::getSchoolId, examDetail.getSchoolId())
                    .eq(ExamDetailCourse::getExamDetailId, examDetailId));
            if (CollectionUtils.isEmpty(examDetailCourseList)) {
                stringSet.add(String.format("印刷计划[%s]没有考点[%s]、考场[%s]的课程数据", examPrintPlan.getName(), examDetail.getExamPlace(), examDetail.getExamRoom()));
            }

            for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
                String courseCode = examDetailCourse.getCourseCode();
                String courseName = examDetailCourse.getCourseName();
                String paperNumber = examDetailCourse.getPaperNumber();
                Long schoolId = examDetailCourse.getSchoolId();

//                List<ExamTask> examTaskList = examTaskMapper.findExamTaskByFlowStatus(schoolId, examPrintPlan.getExamId(), courseCode, paperNumber, FlowStatusEnum.FINISH.name());
//                if (CollectionUtils.isEmpty(examTaskList)) {
//                    stringSet.add(String.format("课程[%s（%s）]，试卷编号[%s]的任务未创建或者未审核通过", courseName, courseCode, paperNumber));
//                }
                //查询命题任务绑定的试卷和题卡
                ExamTask examTask = examTaskService.getByExamIdAndCourseCodeAndPaperNumber(examPrintPlan.getExamId(), examDetailCourse.getCourseCode(), examDetailCourse.getPaperNumber());
                if (examTask == null) {
                    stringSet.add(String.format("课程[%s（%s）]，试卷编号[%s]的" + ExceptionResultEnum.EXAM_TASK_IS_NOT_EXIST.getMessage(), courseName, courseCode, paperNumber));
                } else {
                    if (examTask.getReview()) {
                        examTask = examTaskService.findExamTaskByFlowStatus(schoolId, examPrintPlan.getExamId(), examDetailCourse.getCourseCode(), examDetailCourse.getPaperNumber(), FlowStatusEnum.FINISH);
                        if (examTask == null) {
                            stringSet.add(String.format("课程[%s（%s）]，试卷编号[%s]的" + ExceptionResultEnum.EXAM_TASK_IS_NULL.getMessage(), courseName, courseCode, paperNumber));
                        }
                    } else {
                        if (!ExamStatusEnum.SUBMIT.equals(examTask.getStatus())) {
                            stringSet.add(String.format("课程[%s（%s）]，试卷编号[%s]的" + ExceptionResultEnum.EXAM_TASK_NOT_SUBMIT.getMessage(), courseName, courseCode, paperNumber));
                        }
                    }
                }
                if (examTask != null && !examTask.getEnable()) {
                    stringSet.add(String.format("课程[%s（%s）]，试卷编号[%s]的" + ExceptionResultEnum.EXAM_TASK_ENABLE.getMessage(), courseName, courseCode, paperNumber));
                }
            }

            examDetailList.add(examDetail);
        }

        // 有异常抛出
        if (!CollectionUtils.isEmpty(stringSet)) {
            throw ExceptionResultEnum.ERROR.exception(String.join("；\r\n", stringSet));
        }

        for (ExamDetail examDetail : examDetailList) {
            TbTaskDetailResult tbTaskDetailResult = examDetailCourseService.getByExamDetailId(examDetail.getId());
            tbTaskPdfService.saveTask(tbTaskDetailResult, examDetail.getPrintPlanId(), user, examDetail.getId());
        }
    }

    @Override
    public List<ExamTaskDetailDto> listTaskPaper(Long semesterId, Long examId, String courseCode, String paperNumber, Long cardRuleId, MakeMethodEnum makeMethod, Long startTime, Long
            endTime) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        // 查询用户角色是否包含命题老师
        List<SysRole> list = sysUserRoleService.listRoleByUserId(sysUser.getId());
//        boolean containsQuestionTeacher = list.stream().filter(m -> RoleTypeEnum.QUESTION_TEACHER.equals(m.getType())).count() > 0;
        boolean containsQuestionTeacher = false;
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, sysUser.getId(), ServletUtil.getRequest().getServletPath());
        return this.baseMapper.listTaskPaper(schoolId, semesterId, examId, courseCode, paperNumber, cardRuleId, makeMethod, startTime, endTime, dpr, orgIds, containsQuestionTeacher, sysUser.getId());
    }

    @Override
    public TBTask taskDownloadPdf(Long semesterId, Long examId, String courseCode, String paperNumber, Long cardRuleId, MakeMethodEnum makeMethod, Long startTime, Long endTime) {
        List<ExamTaskDetailDto> examTasks = this.listTaskPaper(semesterId, examId, courseCode, paperNumber, cardRuleId, makeMethod, startTime, endTime);
        if (CollectionUtils.isEmpty(examTasks)) {
            throw ExceptionResultEnum.ERROR.exception("没有可导出数据");
        }
        Map<String, Object> map = tbTaskService.saveTask(TaskTypeEnum.PAPER_AND_CARD_PDF_DOWNLOAD, null, (SysUser) ServletUtil.getRequestUser());
        map.put("examTasks", examTasks);
        asyncPaperReviewPdfExportService.exportTask(map);
        TBTask tbTask = Objects.nonNull(map.get(SystemConstant.TASK)) ? (TBTask) map.get(SystemConstant.TASK) : null;
        return tbTask;
    }

    /**
     * 获取待办任务
     *
     * @param iPage
     * @param schoolId
     * @param orgId
     * @param userId
     * @return
     */
    @Override
    public IPage<WorkResult> getFlowTaskReadyList(IPage<Map> iPage, Long schoolId, Long orgId, Long userId) {
        return this.baseMapper.getFlowTaskReadyList(iPage, schoolId, orgId, String.valueOf(userId));
    }

    /**
     * 根据流程id和任务节点id获取待办
     *
     * @param flowId
     * @param taskId
     * @return
     */
    @Override
    public WorkResult getFlowInfo(Long flowId, Long taskId) {
        return this.baseMapper.getFlowInfo(flowId, taskId);
    }

    /**
     * 根据流程状态查找命题任务
     *
     * @param schoolId
     * @param courseCode
     * @param paperNumber
     * @param flowStatus
     * @return
     */
    @Override
    public ExamTask findExamTaskByFlowStatus(Long schoolId, Long examId, String courseCode, String paperNumber, FlowStatusEnum flowStatus) {
        List<ExamTask> examTaskList = this.baseMapper.findExamTaskByFlowStatus(schoolId, examId, courseCode, paperNumber, Objects.nonNull(flowStatus) ? flowStatus.name() : null);
        if (CollectionUtils.isEmpty(examTaskList)) {
            throw ExceptionResultEnum.EXAM_TASK_IS_NULL.exception();
        } else if (examTaskList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception("命题任务查询异常：" + ExceptionResultEnum.DATA_COUNT_EXCEPTION.getMessage() + String.format("考试ID[%s]，课程代码[%s]，试卷编号[%s]", examId, courseCode, paperNumber));
        } else {
            ExamTask examTask = examTaskList.get(0);
            if (!examTask.getEnable()) {
                throw ExceptionResultEnum.EXAM_TASK_ENABLE.exception();
            }

            return examTask;
        }
    }

    @Transactional
    @Override
    public void removeByExamTaskId(Long examTaskId) {
        // 删除任务
        this.removeById(examTaskId);
        QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskId);
        ExamTaskDetail examTaskDetail = examTaskDetailService.getOne(queryWrapper);
        if (examTaskDetail != null) {
            examTaskDetailService.removeById(examTaskDetail.getId());

            // 题卡更新为未使用
            String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
            if (StringUtils.isNotBlank(paperAttachmentIds)) {
                List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(paperAttachmentIds);
                for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                    if (StringUtils.isBlank(paperInfoVo.getCardId())) {
                        continue;
                    }
                    Long cardId = Long.valueOf(paperInfoVo.getCardId());
                    ExamCard examCard = examCardService.getById(cardId);
                    if (examCard.getType().equals(CardTypeEnum.CUSTOM) || (examCard.getType().equals(CardTypeEnum.GENERIC) && examCard.getCreateMethod().equals(CardCreateMethodEnum.STANDARD))) {
                        UpdateWrapper<ExamCard> updateWrapper = new UpdateWrapper<>();
                        updateWrapper.lambda().set(ExamCard::getUsed, false).eq(ExamCard::getId, cardId);
                        examCardService.update(updateWrapper);
                    }
                }
            }
        }
    }

    /**
     * 校验输入的试卷编号是否存在
     *
     * @param schoolId
     * @param examTask
     */
    public void validPaperNumberExists(Long schoolId, ExamTask examTask) {
        boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_PAPER_NUMBER_PREFIX + schoolId + examTask.getExamId() + examTask.getPaperNumber(), SystemConstant.REDIS_LOCK_PAPER_NUMBER_TIME_OUT);
        if (!lock) {
            throw ExceptionResultEnum.ERROR.exception("正在校验试卷编号中，请稍候再试！");
        }
        try {
            QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
            taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId)
                    .eq(ExamTask::getExamId, examTask.getExamId())
                    .eq(ExamTask::getPaperNumber, examTask.getPaperNumber());
            if (Objects.nonNull(examTask.getId())) {
                taskQueryWrapper.lambda().ne(ExamTask::getId, examTask.getId());
            }
            ExamTask task = this.getOne(taskQueryWrapper);
            if (task != null) {
                throw ExceptionResultEnum.ERROR.exception("试卷编号已存在");
            }
        } finally {
            redisUtil.releaseLock(SystemConstant.REDIS_LOCK_PAPER_NUMBER_PREFIX + schoolId + examTask.getExamId() + examTask.getPaperNumber());
        }
    }

    @Override
    public String findCardTitle(Long cardRuleId) {
        String title = "通用题卡";
        if (-1L != cardRuleId) {
            BasicCardRule basicCardRule = basicCardRuleService.getById(cardRuleId);
            if (Objects.isNull(basicCardRule)) {
                throw ExceptionResultEnum.ERROR.exception("未找到题卡规则id");
            }
            title = basicCardRule.getTitleRule();
            // TODO: 2021/8/16 title中的动态变量替换规则
        }
        return title;
    }

    @Transactional
    @Override
    public Map<String, Object> submitTaskApply(ExamTaskApplyTemp examTaskApplyTemp) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        JSONObject jsonObject = JSONObject.parseObject(examTaskApplyTemp.getExamTaskContent());
        // 新建命题任务
        ExamTask examTask = JSONObject.parseObject(String.valueOf(jsonObject.get("examTask")), ExamTask.class);
        BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(examTask.getExamId());

        if (basicPrintConfig.getReview()) {
            Long customFlowId = examTaskApplyTemp.getCustomFlowId();
            Optional.ofNullable(customFlowId).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("自定义流程ID为空"));

            if (Objects.nonNull(examTaskApplyTemp.getVersion())) {
                Integer version = examTaskApplyTemp.getVersion();
                Optional.ofNullable(version).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("自定义流程版本号为空"));

                TFCustomFlow tfCustomFlow = tfCustomFlowService.getById(customFlowId);
                Optional.ofNullable(tfCustomFlow).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未找到自定义流程数据"));

                if (tfCustomFlow.getVersion().intValue() != version.intValue()) {
                    throw ExceptionResultEnum.ERROR.exception("自定义流程数据已发生改变，请刷新重试");
                }
            }
        }

        Map<String, Object> mapVar = new HashMap<>();
        try {
            if (StringUtils.isNotBlank(examTask.getPaperNumber())) {
                validPaperNumberExists(schoolId, examTask);
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                String paperNumber = printCommonService.createPaperNumber(schoolId, examTask.getExamId());
                examTask.setPaperNumber(paperNumber);
            }

            String sequence = printCommonService.createCourseSequence(schoolId, examTask.getCourseCode());
            examTask.setSequence(sequence);
            examTask.setId(SystemConstant.getDbUuid());
            examTask.setSchoolId(schoolId);
            examTask.setOrgId(basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, examTask.getCourseCode()));
            examTask.setReview(basicPrintConfig.getReview());
            examTask.setUserId(sysUser.getId());
            examTask.setStatus(ExamStatusEnum.SUBMIT);
            examTask.setCreateId(sysUser.getId());
            examTask.setCreateTime(System.currentTimeMillis());

            examTask.setCardRuleId(basicPrintConfig.getCardRuleId());

            if (Objects.isNull(examTask.getFlowId())) {
                if (Objects.nonNull(basicPrintConfig.getReview()) && basicPrintConfig.getReview()) {
                    Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                            examTask,
                            TFCustomTypeEnum.ELECTRON_FLOW,
                            examTask.getUserId(),
                            false);
                    map = activitiService.customFlowStart(map);
                    examTask.setFlowId(SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID)));
                    map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.NEW);
                    tfFlowJoinService.saveOrUpdate(map);
                    mapVar.putAll(map);
                    mapVar.put(SystemConstant.SEND_FLOW_START_MQ, true);
                }
            }
            this.save(examTask);

            // 新建试卷
            ExamTaskDetail examTaskDetail = JSONObject.parseObject(String.valueOf(jsonObject.get("examTaskDetail")), ExamTaskDetail.class);
            validSubmitParam(examTaskDetail, basicPrintConfig.getPrintContent());
            // 已曝光试卷和未曝光试卷赋值（新增时，已曝光试卷为null，未曝光试卷为paper_type）
            examTaskDetail.setId(SystemConstant.getDbUuid());
            examTaskDetail.setUnexposedPaperType(examTaskDetail.getPaperType());
            examTaskDetail.setExamTaskId(examTask.getId());
            examTaskDetail.setCreateId(sysUser.getId());
            examTaskDetail.setCreateTime(System.currentTimeMillis());
            examTaskDetailService.save(examTaskDetail);

            String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
            List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(paperAttachmentIds);
            // 将题卡更新为已绑定
            for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                if (CardTypeEnum.CUSTOM.name().equals(paperInfoVo.getCardType())) {
                    UpdateWrapper<ExamCard> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().set(ExamCard::getUsed, true).eq(ExamCard::getId, paperInfoVo.getCardId());
                    examCardService.update(updateWrapper);
                }
            }
            // 删除临时题卡
            for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                if (paperInfoVo.getPaperId() != null) {
                    QueryWrapper<ExamCard> deleteWrapper = new QueryWrapper<>();
                    deleteWrapper.lambda().eq(ExamCard::getCreateId, sysUser.getId())
                            .eq(ExamCard::getPaperId, paperInfoVo.getPaperId())
                            .eq(ExamCard::getUsed, false);
                    examCardService.remove(deleteWrapper);
                }
            }
            // 更新题库试卷表
            for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                if (StringUtils.isNotBlank(paperInfoVo.getUuid())) {
                    UpdateWrapper<ExamTaskPaperData> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().set(ExamTaskPaperData::getPaperNumber, examTask.getPaperNumber())
                            .eq(ExamTaskPaperData::getExamId, examTask.getExamId())
                            .eq(ExamTaskPaperData::getPaperNumber, paperInfoVo.getUuid())
                            .eq(ExamTaskPaperData::getPaperId, paperInfoVo.getPaperId())
                            .eq(ExamTaskPaperData::getUuid, paperInfoVo.getUuid());
                    examTaskPaperDataService.update(updateWrapper);

                    // 删除临时数据
                    QueryWrapper<ExamTaskPaperData> deleteWrapper = new QueryWrapper<>();
                    deleteWrapper.lambda().eq(ExamTaskPaperData::getExamId, examTask.getExamId())
                            .eq(ExamTaskPaperData::getPaperNumber, paperInfoVo.getUuid())
                            .ne(ExamTaskPaperData::getPaperId, paperInfoVo.getPaperId());
                    examTaskPaperDataService.remove(deleteWrapper);
                }
            }

            BasicExam basicExam = basicExamService.getById(examTask.getExamId());
            if (ExamModelEnum.MODEL1.equals(basicExam.getExamModel()) || ExamModelEnum.MODEL2.equals(basicExam.getExamModel())) {
                // 创建印刷计划
                ExamDetailParams examDetailParams = JSONObject.parseObject(String.valueOf(jsonObject.get("examDetail")), ExamDetailParams.class);
                ExamPrintPlan examPrintPlan = new ExamPrintPlan();
                examPrintPlan.insertInfo(sysUser.getId());
                examPrintPlan.setSchoolId(schoolId);
                examPrintPlan.setExamId(examTask.getExamId());
                examPrintPlan.setOrgId(sysUser.getOrgId());
                examPrintPlan.setName(examTask.getCourseName() + examTask.getPaperNumber());
                if (!ExamCategoryEnum.REBUILD.equals(basicExam.getCategory())) {
                    ConvertUtil.analyzeAndCompareDateAndTime(examDetailParams.getExamStartTime(), examDetailParams.getExamEndTime());
                    examPrintPlan.setExamStartTime(examDetailParams.getExamStartTime());
                    examPrintPlan.setExamEndTime(examDetailParams.getExamEndTime());
                }
                List<String> stringList = JSONObject.parseArray(basicPrintConfig.getPrintContent(), String.class);
                if (!stringList.contains("PAPER")) {
                    long count = paperInfoVoList.stream().filter(m -> Objects.isNull(m.getAttachmentId())).count();
                    if (count == 0) {
                        stringList.add("PAPER");
                    }
                }
                examPrintPlan.setPrintContent(String.join(",", stringList));
                examPrintPlan.setBackupMethod(BackupMethodEnum.valueOf(basicPrintConfig.getBackupMethod()));
                examPrintPlan.setBackupCount(basicPrintConfig.getBackupCount());
                examPrintPlan.setDrawRule(basicPrintConfig.getDrawRule());
                examPrintPlan.setVariableContent(basicPrintConfig.getVariableContent());
                examPrintPlan.setOrdinaryContent(basicPrintConfig.getOrdinaryContent());
                examPrintPlan.setStatus(PrintPlanStatusEnum.READY);
                examPrintPlanService.save(examPrintPlan);

                // 查询考试+课程下已用过的班级
                Map<String, String> usedClassIdsMap = new HashMap<>();
                List<ExamDetailCourseDto> examDetailCourseList = examDetailService.listByExamIdAndCourseCode(schoolId, examTask.getExamId(), examTask.getCourseCode());
                if (!CollectionUtils.isEmpty(examDetailCourseList)) {
                    examDetailCourseList.stream().filter(m -> StringUtils.isNotBlank(m.getClazzId())).forEach(m -> {
//                        usedClassIds.addAll(Arrays.asList(m.getClazzId().split(",")));
                        for (String s : m.getClazzId().split(",")) {
                            usedClassIdsMap.put(s, String.format("%s(%s)", m.getRealName(), m.getLoginName()));
                        }
                    });
                }
                Set<String> classIdSet = usedClassIdsMap.keySet();

                // 已使用过的班级集合（报错用）
                List<String> errorClassIds = new ArrayList<>();

                String paperNumber = examTask.getPaperNumber();
                if (ExamModelEnum.MODEL1.equals(basicExam.getExamModel())) {
                    List<ExamDetailList> examDetailLists = JSONObject.parseArray(JSONObject.toJSONString(examDetailParams.getList()), ExamDetailList.class);
                    for (ExamDetailList examDetailList : examDetailLists) {
                        int count = examDetailList.getStudentCount();
                        String clazzName = examDetailList.getClassName();
                        if (count == 0) {
                            throw ExceptionResultEnum.ERROR.exception("选择的考试班级[" + clazzName + "]下无学生，请确认该班级学生信息");
                        }

                        if (!CollectionUtils.isEmpty(classIdSet)) {
                            for (String s : examDetailList.getClassId().split(",")) {
                                if (classIdSet.contains(s)) {
                                    errorClassIds.add(s);
                                }
                            }
                        }

                        ExamDetail examDetail = new ExamDetail();
                        examDetail.setId(SystemConstant.getDbUuid());
                        examDetail.setPackageCode("1" + redisCounterUtil.getCounter(schoolId, 6, "packageCode"));
                        examDetail.setSchoolId(schoolId);
                        examDetail.setOrgId(sysUser.getOrgId());
                        examDetail.setPrintPlanId(examPrintPlan.getId());
                        examDetail.setExamId(examPrintPlan.getExamId());
                        examDetail.setPrintPlanName(examPrintPlan.getName());
                        examDetail.setExamPlace(examDetailList.getExamPlace()); // 无默认值
                        examDetail.setExamRoom(examDetailList.getExamRoom()); // 无默认值
                        examDetail.setStatus(ExamDetailStatusEnum.NEW);
                        if (!ExamCategoryEnum.REBUILD.equals(basicExam.getCategory())) {
                            examDetail.setExamStartTime(examDetailParams.getExamStartTime());
                            examDetail.setExamEndTime(examDetailParams.getExamEndTime());
                        }
                        examDetail.setTotalSubjects(count);
                        examDetail.setExamDataSource(ExamDataSourceEnum.EXAM_TASK);
                        examDetail.setNormal(true);
                        examDetail.setPrintHouseId(examDetailList.getPrintHouseId());
                        examDetail.setBackupCount(examDetailList.getBackupCount());
                        examDetail.setCreateId(sysUser.getId());
                        examDetail.setCreateTime(System.currentTimeMillis());
                        examDetailService.save(examDetail);

                        ExamDetailCourse examDetailCourse = new ExamDetailCourse();
                        examDetailCourse.setId(SystemConstant.getDbUuid());
                        examDetailCourse.setSchoolId(schoolId);
                        examDetailCourse.setExamDetailId(examDetail.getId());
                        examDetailCourse.setCourseCode(examTask.getCourseCode());
                        examDetailCourse.setCourseName(examTask.getCourseName());
                        examDetailCourse.setPaperNumber(paperNumber);
                        examDetailCourse.setCoursePaperId(String.valueOf(examTask.getId()));
                        examDetailCourse.setClazzId(examDetailList.getClassId());
                        examDetailCourse.setTotalSubjects(examDetailList.getStudentCount());
                        examDetailCourse.setCreateId(sysUser.getId());

                        // 更新实际考生数量
                        List<ExamTaskStudentObjectParam> examTaskStudentObjectParamList = examDetailList.getExamTaskStudentObjectParamList();
                        List<ExamStudent> examStudents = printCommonService.createBatchStudentByStudentList(examPrintPlan.getExamId(), examTask, examDetailList.getExtendFields(), examTaskStudentObjectParamList, examDetailCourse.getId(), sysUser);

                        Set<String> classNameSet = new HashSet<>();
                        for (ExamStudent examStudent : examStudents) {
                            classNameSet.add(examStudent.getTeachClazzName());
                        }

                        examDetailCourse.setClazzName(!CollectionUtils.isEmpty(classNameSet) ? String.join(",", classNameSet) : examDetailList.getClassName());
                        examDetailCourseService.save(examDetailCourse);

                        if (!CollectionUtils.isEmpty(examStudents)) {
                            examStudentService.saveBatch(examStudents);
                        }
                        int studentCount = examStudents.size();

                        if (studentCount - examDetail.getTotalSubjects() != 0) {
                            UpdateWrapper<ExamDetail> updateWrapper = new UpdateWrapper<>();
                            updateWrapper.lambda().set(ExamDetail::getTotalSubjects, studentCount).eq(ExamDetail::getId, examDetail.getId());
                            examDetailService.update(updateWrapper);
                        }
                        if (studentCount - examDetailCourse.getTotalSubjects() != 0) {
                            UpdateWrapper<ExamDetailCourse> updateWrapper = new UpdateWrapper<>();
                            updateWrapper.lambda().set(ExamDetailCourse::getTotalSubjects, studentCount).eq(ExamDetailCourse::getId, examDetailCourse.getId());
                            examDetailCourseService.update(updateWrapper);
                        }
                    }
                } else if (ExamModelEnum.MODEL2.equals(basicExam.getExamModel())) {
                    if (StringUtils.isNotBlank(examDetailParams.getClassId()) && !CollectionUtils.isEmpty(classIdSet)) {
                        for (String s : examDetailParams.getClassId().split(",")) {
                            if (classIdSet.contains(s)) {
                                errorClassIds.add(s);
                            }
                        }
                    }
                    ExamDetail examDetail = new ExamDetail();
                    examDetail.setId(SystemConstant.getDbUuid());
                    examDetail.setPackageCode("1" + redisCounterUtil.getCounter(schoolId, 6, "packageCode"));
                    examDetail.setSchoolId(schoolId);
                    examDetail.setOrgId(sysUser.getOrgId());
                    examDetail.setPrintPlanId(examPrintPlan.getId());
                    examDetail.setPrintPlanName(examPrintPlan.getName());
                    examDetail.setExamId(examPrintPlan.getExamId());
//                    examDetail.setExamPlace("考点1"); // 默认填充班级所在校区
//                    examDetail.setExamRoom("考场1"); // 默认填充班级
                    examDetail.setStatus(ExamDetailStatusEnum.NEW);
                    if (!ExamCategoryEnum.REBUILD.equals(basicExam.getCategory())) {
                        examDetail.setExamStartTime(examDetailParams.getExamStartTime());
                        examDetail.setExamEndTime(examDetailParams.getExamEndTime());
                    }
                    examDetail.setTotalSubjects(examDetailParams.getTotalSubjects());
                    examDetail.setExamDataSource(ExamDataSourceEnum.EXAM_TASK);
                    examDetail.setNormal(true);
                    examDetail.setPrintHouseId(examDetailParams.getPrintHouseId());
                    examDetail.setBackupCount(examDetailParams.getBackupCount());
                    examDetail.setCreateId(sysUser.getId());
                    examDetail.setCreateTime(System.currentTimeMillis());
                    examDetailService.save(examDetail);

                    ExamDetailCourse examDetailCourse = new ExamDetailCourse();
                    examDetailCourse.setId(SystemConstant.getDbUuid());
                    examDetailCourse.setSchoolId(schoolId);
                    examDetailCourse.setExamDetailId(examDetail.getId());
                    examDetailCourse.setCourseCode(examTask.getCourseCode());
                    examDetailCourse.setCourseName(examTask.getCourseName());
                    examDetailCourse.setPaperNumber(paperNumber);
                    examDetailCourse.setClazzId(examDetailParams.getClassId());
                    if (StringUtils.isNotBlank(examDetailParams.getClassId())) {
                        List<BasicTeachClazz> basicClazzList = basicTeachClazzService.listByIds(Arrays.asList(examDetailParams.getClassId().split(",")));
                        examDetailCourse.setClazzName(basicClazzList.stream().map(BasicTeachClazz::getClazzName).collect(Collectors.joining(",")));
                    }
                    examDetailCourse.setTotalSubjects(examDetailParams.getTotalSubjects());
                    examDetailCourse.setCreateId(sysUser.getId());
                    examDetailCourseService.save(examDetailCourse);
                }

                if (!CollectionUtils.isEmpty(errorClassIds)) {
                    // 选择学生时，不校验
                    boolean containsPrivilege = teachcloudCommonService.containsPrivilege(sysUser.getId(), "button", "SelectStudent");
                    if (!containsPrivilege || ExamCategoryEnum.REBUILD.equals(basicExam.getCategory())) {
                        Map<String, List<String>> finalMap = new HashMap<>();
                        StringJoiner stringJoiner = new StringJoiner(",");
                        List<BasicTeachClazz> basicClazzList = basicTeachClazzService.listByIds(errorClassIds);
                        for (BasicTeachClazz basicClazz : basicClazzList) {
                            String key = usedClassIdsMap.get(basicClazz.getId().toString());
                            List<String> value;
                            if (finalMap.containsKey(key)) {
                                value = finalMap.get(key);
                            } else {
                                value = new ArrayList<>();
                            }
                            value.add(basicClazz.getClazzName());
                            finalMap.put(key, value);
                        }
                        for (Map.Entry<String, List<String>> entry : finalMap.entrySet()) {
                            stringJoiner.add("考试对象[" + String.join(",", entry.getValue()) + "]已被" + entry.getKey() + "选择");
                        }
                        if (StringUtils.isNotBlank(stringJoiner.toString())) {
                            throw ExceptionResultEnum.ERROR.exception(stringJoiner.toString());
                        }
                    }
                }
            }

            if (Objects.nonNull(examTask.getFlowId())) {
                // 审核一级
                Task task = taskService.createTaskQuery().processInstanceId(String.valueOf(examTask.getFlowId())).singleResult();
                if (Objects.nonNull(task)) {
                    Map<String, Object> map = new HashMap<>();
                    map.computeIfAbsent(SystemConstant.FLOW_TASK_ID, v -> task.getId());
                    map.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
                    map.computeIfAbsent(SystemConstant.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
                    map.computeIfAbsent(SystemConstant.APPROVE_USER_IDS, v -> examTaskApplyTemp.getApproveUserIds());
                    activitiService.taskApprove(map);
                    mapVar.putAll(map);
                    mapVar.remove(SystemConstant.SEND_FLOW_START_MQ);
                    mapVar.put(SystemConstant.SEND_FLOW_MQ, true);
                }
            }
            mapVar.put(SystemConstant.EXAM_TASK, examTask);
            return mapVar;
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ActivitiObjectNotFoundException) {
                ResultUtil.error("流程不存在");
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return null;
    }

    @Override
    public ExamTaskApprovalFormDto findExamTaskApprovalForm(Long examTaskId, Long examId, String paperNumber) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        // 1.basicInfo 部分
        ExamTask examTask = null;
        if (examTaskId != null) {
            examTask = this.getById(examTaskId);
        } else if (examId != null && StringUtils.isNotBlank(paperNumber)) {
            examTask = this.getByExamIdAndCourseCodeAndPaperNumber(examId, null, paperNumber);
        } else {
            throw ExceptionResultEnum.ERROR.exception("传入参数有误");
        }
        if (Objects.isNull(examTask)) {
            throw ExceptionResultEnum.ERROR.exception("命题任务不存在");
        }
        String paperName = basicSemesterService.getById(basicExamService.getById(examTask.getExamId()).getSemesterId()).getName();
        Long teachingRoomId = examTask.getTeachingRoomId();
        SysOrg teachingRoom = sysOrgService.getOne(new QueryWrapper<SysOrg>().lambda().eq(SysOrg::getId, teachingRoomId));
        if (Objects.isNull(teachingRoom)) {
            throw ExceptionResultEnum.ERROR.exception("找不到教研室");
        }
        String teachingRoomName = teachingRoom.getName();

        // 查询教研室所在学院
        List<SysOrg> orgList = sysOrgService.findParentsByOrgId(teachingRoom.getId());
        List<SysOrg> schoolOrgList = orgList.stream().filter(e -> OrgTypeEnum.SCHOOL.equals(e.getType())).collect(Collectors.toList());
        if (schoolOrgList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception("学校级机构信息异常");
        }
        SysOrg schoolOrg = schoolOrgList.get(0);
        List<SysOrg> collegeList = orgList.stream().filter(e -> Objects.equals(schoolOrg.getId(), e.getParentId())).collect(Collectors.toList());
        SysOrg college = new SysOrg();
        if (collegeList.size() == 1) {
            college = collegeList.get(0);
        }

        // 流程id
        Long flowId = examTask.getFlowId();

        // 3.ApprovalInfo部分
        Boolean review = examTask.getReview();
        List<ApprovalInfo> approvalInfoList = new ArrayList<>();
        if (review) {
            approvalInfoList = tfFlowLogService.findApprovalInfoByFlowId(flowId);
        }

        // 2.ExamRoomInfo 部分
        List<ExamPackageDetail> examPackageDetailDatasource = this.baseMapper.findExamPackageDetailByPaperNumber(schoolId, examTask.getExamId(), examTask.getCourseCode(), examTask.getPaperNumber(), null);
        if (examPackageDetailDatasource.size() == 0) {
            throw ExceptionResultEnum.ERROR.exception("缺少该命题任务对应的考务数据");
        }
        ExamObjectDto examObjectDto = this.buildExamObjectDtoByPackageDatasource(examPackageDetailDatasource);

        // ---- 组装Dto
        ExamTaskApprovalFormDto examTaskApprovalFormDto = new ExamTaskApprovalFormDto();
        BasicInfo basicInfo = new BasicInfo();
        if (Objects.nonNull(college)) {
            basicInfo.setCollegeId(college.getId());
            basicInfo.setCollegeName(college.getName());
        }
        basicInfo.setTeachingRoomId(teachingRoomId);
        basicInfo.setTeachingRoomName(teachingRoomName);
        basicInfo.setPaperName(paperName);
        basicInfo.setPaperNumber(examTask.getPaperNumber());
        basicInfo.setTeacherName(examTask.getTeacherName());
        basicInfo.setExamStartTime(examObjectDto.getExamStartTime());
        basicInfo.setExamEndTime(examObjectDto.getExamEndTime());
        basicInfo.setCourseName(examTask.getCourseName());

        examTaskApprovalFormDto.setBasicInfo(basicInfo);
        examTaskApprovalFormDto.setExamRoomInfoList(examObjectDto.getExamRoomInfoList());
        examTaskApprovalFormDto.setApprovalInfoList(approvalInfoList);
        return examTaskApprovalFormDto;
    }

    @Transactional
    @Override
    public void findExamTaskApprovalPdf(HttpServletResponse response, Long examTaskId, String htmlContext) throws Exception {
        ExamTask examTask = this.getById(examTaskId);
        if (Objects.isNull(examTask)) {
            throw ExceptionResultEnum.ERROR.exception("未找到命题任务");
        }
        String fileName = examTask.getPaperNumber() + "审批表";
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        Long approveFormAttachmentId = examTask.getApproveFormAttachmentId();
        BasicAttachment basicAttachment = basicAttachmentService.getById(approveFormAttachmentId);
        if (Objects.isNull(basicAttachment)) {
            // 命题任务试卷审批表字段为空 -> 保存html并生成pdf附件
            basicAttachment = printCommonService.saveAttachmentHtmlAndPdf(fileName, htmlContext, sysUser.getId());
            examTask.setApproveFormAttachmentId(basicAttachment.getId());
            this.updateById(examTask);
        }
        JSONObject jsonObject = JSONObject.parseObject(basicAttachment.getPath());
        String pdfPath = (String) jsonObject.get(SystemConstant.PDF_PATH);
        String type = (String) jsonObject.get(SystemConstant.TYPE);
        InputStream inputStream = null;
        if (Objects.equals(type, SystemConstant.OSS)) {
            inputStream = fileStoreUtil.ossDownloadIs(pdfPath, fileStoreUtil.getUploadEnumByPath(pdfPath).getFssType());
        } else {
            inputStream = new FileInputStream(new File(pdfPath));
        }
        ConvertUtil.outputFile(response, inputStream, fileName + SystemConstant.PDF_PREFIX);
    }

    @Transactional
    @Override
    public ExamObjectDto findExamObjectDtoByPaperNumber(Long examId, String paperNumber) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        ExamTask examTask = examTaskService.getOne(new QueryWrapper<ExamTask>().lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getExamId, examId).eq(ExamTask::getPaperNumber, paperNumber));
        if (Objects.isNull(examTask)) {
            throw ExceptionResultEnum.ERROR.exception("未找到命题任务");
        }
        List<ExamPackageDetail> examPackageDetailDatasource = this.baseMapper.findExamPackageDetailByPaperNumber(schoolId, examTask.getExamId(), examTask.getCourseCode(), paperNumber, null);
        ExamObjectDto examObjectDto = this.buildExamObjectDtoByPackageDatasource(examPackageDetailDatasource);

        examObjectDto.setPaperName(examTask.getPaperName());
        examObjectDto.setPaperNumber(examTask.getPaperNumber());
        return examObjectDto;
    }

    /**
     * 根据流程id查找examtask
     *
     * @param flowId
     * @return
     */
    @Override
    public ExamTask findByFlowId(Long flowId) {
        ExamTask examTask = this.baseMapper.findByFlowId(flowId);
        Optional.ofNullable(examTask).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("命题计划为空"));
        return examTask;
    }

    @Transactional
    @Override
    public Map<String, Object> applyResave(ExamTaskReApplyParam examTaskReApplyParam) {
        ExamTaskDetail examTaskDetail = examTaskReApplyParam.getExamTaskDetail();
        UpdateWrapper<ExamTaskDetail> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamTaskDetail::getPaperAttachmentIds, examTaskDetail.getPaperAttachmentIds()).eq(ExamTaskDetail::getExamTaskId, examTaskDetail.getExamTaskId());
        examTaskDetailService.update(updateWrapper);

        String flowTaskId = examTaskReApplyParam.getFlowTaskId();

        Map<String, Object> map = new HashMap<>();
        map.putIfAbsent(SystemConstant.FLOW_TASK_ID, flowTaskId);
        map.computeIfAbsent(SystemConstant.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
        map.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
        activitiService.taskApprove(map);
        return map;
    }

    @Override
    public ExamTaskDto getByExamTaskId(Long examTaskId) {
        return this.baseMapper.getByExamTaskId(examTaskId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<ExamTaskStudentObjectResult> findExamTaskStudentObject(Long examId, String courseCode, String teacher, String className, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        List<ExamTaskStudentObjectResult> result = new ArrayList<>();
        // 基础班级对象
        List<BasicExamStudentResult> basicStudentResultList = basicStudentService.basicStudentList(schoolId, examId, courseCode, teacher, className, null);
        if (!CollectionUtils.isEmpty(basicStudentResultList)) {
            LinkedMultiValueMap<Long, StudentInfo> studentInfoLinkedMultiValueMap = new LinkedMultiValueMap<>();
            Map<Long, ExamTaskStudentObjectResult> examTaskStudentObjectResultMap = new LinkedHashMap<>();
            for (BasicExamStudentResult e : basicStudentResultList) {
                // 没有班级的考生，不返回
                if (StringUtils.isBlank(e.getClazz())) {
                    continue;
                }
                StudentInfo studentInfo = new StudentInfo();
                studentInfo.setStudentId(e.getId());
                studentInfo.setStudentCode(e.getStudentCode());
                studentInfo.setStudentName(e.getStudentName());
                studentInfo.setCollegeName(e.getCollegeName());
                studentInfo.setMajorName(e.getMajorName());
                studentInfo.setBasicClazzId(e.getClazzId());
                studentInfo.setBasicClazzName(e.getClazz());
                studentInfoLinkedMultiValueMap.add(e.getClazzId(), studentInfo);

                ExamTaskStudentObjectResult examTaskStudentObjectResult = null;
                if (!examTaskStudentObjectResultMap.containsKey(e.getClazzId())) {
                    examTaskStudentObjectResult = new ExamTaskStudentObjectResult();
                    examTaskStudentObjectResult.setClazzId(e.getClazzId());
                    examTaskStudentObjectResult.setClazzName(e.getClazz());
                } else {
                    examTaskStudentObjectResult = examTaskStudentObjectResultMap.get(e.getClazzId());
                }
                examTaskStudentObjectResult.setStudentInfoList(studentInfoLinkedMultiValueMap.get(e.getClazzId()));
                examTaskStudentObjectResultMap.put(e.getClazzId(), examTaskStudentObjectResult);
            }
            result.addAll(examTaskStudentObjectResultMap.values());
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<ExamTaskExamStudentImportResult> examTaskExamStudentImport(MultipartFile file, SysUser requestUser) throws IOException, NoSuchFieldException, IllegalAccessException {
        if (Objects.isNull(file)) {
            throw ExceptionResultEnum.ERROR.exception("缺少导入文件");
        }
        // excel 读取和检验
        List<ExamStudentImportDto> examStudentImportDtoList = new ArrayList<>();
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamStudentImportDto.class, DescribeImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            List<ExcelError> excelErrorTemp = new ArrayList<>();
            Map<String, String> checkCodeMap = new HashMap<>();
            Map<String, String> checkPhoneMap = new HashMap<>();
            for (int i = 0; i < finalExcelList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                List<Object> examStudentImportDtoDatasource = excelMap.get(i);
                assert examStudentImportDtoDatasource != null;
                if (examStudentImportDtoDatasource.get(0) instanceof DescribeImportDto) {
                    continue;
                }
                for (int y = 0; y < Objects.requireNonNull(examStudentImportDtoDatasource).size(); y++) {
                    ExamStudentImportDto examStudentImportDto = (ExamStudentImportDto) examStudentImportDtoDatasource.get(y);
                    String studentName = examStudentImportDto.getStudentName();
                    String studentCode = examStudentImportDto.getStudentCode();
                    String phoneNumber = examStudentImportDto.getPhoneNumber();
                    String clazzName = examStudentImportDto.getClazzName();
                    String examPlace = examStudentImportDto.getExamPlace();
                    String examRoom = examStudentImportDto.getExamRoom();
                    examStudentImportDtoList.add(examStudentImportDto);


                    // 检验excel中
                    // 检验学号
                    if (SystemConstant.strNotNull(studentCode)) {
                        if (checkCodeMap.containsKey(studentCode)) {
                            throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的学生编号[" + studentCode + "]");
                        } else {
                            checkCodeMap.put(studentCode, studentName);
                        }
                    }
                    // 如果电话有值则检验电话excel中唯一性
                    if (SystemConstant.strNotNull(phoneNumber)) {
                        if (checkPhoneMap.containsKey(phoneNumber)) {
                            throw ExceptionResultEnum.ERROR.exception("导入的excel中包含在重复的电话号码[" + phoneNumber + "]");
                        } else {
                            checkPhoneMap.put(phoneNumber, studentCode);
                        }
                    }

                    excelErrorTemp.addAll(ExcelUtil.checkExcelField(examStudentImportDto, y, i));
                    if (Objects.nonNull(studentCode) && !studentCode.matches(SystemConstant.REGULAR_EXPRESSION_OF_CODE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[学号]不符合输入规范"));
                    }
                    if (Objects.nonNull(phoneNumber) && !phoneNumber.matches(SystemConstant.REGULAR_EXPRESSION_OF_PHONE)) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[电话号码]不符合输入规范"));
                    }
                }
            }
            if (excelErrorTemp.size() > 0) {
                List<String> errors = excelErrorTemp.stream().map(ExcelError::getExcelErrorType).collect(Collectors.toList());
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
            }
            return finalExcelList;
        }, 2);
        // 校验已经有的考生所在部门的信息是否正确
        // 导入的excel创建或查询的学生集合
        List<BasicExamStudentResult> studentDatasource = basicStudentService.basicStudentList(requestUser.getSchoolId(), null, null, null, null, null);

        List<ExamStudentImportDto> existStudentList = examStudentImportDtoList
                .stream()
                .filter(e -> studentDatasource.stream().map(BasicExamStudentResult::getStudentCode).collect(Collectors.toList()).contains(e.getStudentCode()))
                .collect(Collectors.toList());
        for (ExamStudentImportDto examStudentImportDto : existStudentList) {
            String studentName = examStudentImportDto.getStudentName();
            String studentCode = examStudentImportDto.getStudentCode();
            String impCollegeName = examStudentImportDto.getCollegeName();
            String impMajorName = examStudentImportDto.getMajorName();
            String impClazzName = examStudentImportDto.getClazzName();


            List<BasicExamStudentResult> basicStudentList = studentDatasource.stream().filter(e -> e.getStudentCode().equals(studentCode)).collect(Collectors.toList());
            if (basicStudentList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("学号为：" + studentCode + "的考生数据异常");
            }
            BasicExamStudentResult basicStudent = basicStudentList.get(0);

            String realityCollegeName = basicStudent.getCollegeName();
            String realityMajorName = basicStudent.getMajorName();
            String realityClazzName = basicStudent.getClazz();

            // 学院核对
            if (!impCollegeName.equals(realityCollegeName)) {
                throw ExceptionResultEnum.ERROR.exception("导入的考生[" + studentName + "(" + studentCode + ")]所在学院[" + impCollegeName +
                        "]与该考生实际所在学院[" + realityCollegeName + "]不符，请检验excel数据是否异常或联系管理员核对");
            }

            // 专业核对
            if (!impMajorName.equals(realityMajorName)) {
                throw ExceptionResultEnum.ERROR.exception("导入的考生[" + studentName + "(" + studentCode + ")]所在专业[" + impMajorName +
                        "]与该考生实际所在专业[" + realityMajorName + "]不符，请检验excel数据是否异常或联系管理员核对");
            }

            // 班级核对
            if (!impClazzName.equals(realityClazzName)) {
                throw ExceptionResultEnum.ERROR.exception("导入的考生[" + studentName + "(" + studentCode + ")]所在班级[" + impClazzName +
                        "]与该考生实际所在班级[" + realityClazzName + "]不符，请检验excel数据是否异常或联系管理员核对");
            }
        }
        // 去掉已存在的学生集合
        List<ExamStudentImportDto> willAddStudentList = new ArrayList<>(examStudentImportDtoList);
        willAddStudentList.removeAll(existStudentList);

        // 创建基础学生信息
        List<BasicStudentExtrasParam> basicStudentExtrasParamList = willAddStudentList.stream().flatMap(e -> {
            BasicStudentExtrasParam basicStudentExtrasParam = new BasicStudentExtrasParam();
            basicStudentExtrasParam.setStudentName(e.getStudentName());
            basicStudentExtrasParam.setStudentCode(e.getStudentCode());
            basicStudentExtrasParam.setPhoneNumber(e.getPhoneNumber());
            basicStudentExtrasParam.setCollegeName(e.getCollegeName());
            basicStudentExtrasParam.setMajorName(e.getMajorName());
            basicStudentExtrasParam.setClazzName(e.getClazzName());
            return Stream.of(basicStudentExtrasParam);
        }).collect(Collectors.toList());
        basicStudentService.saveBasicStudentWithExtrasList(basicStudentExtrasParamList, requestUser, false, null);


        // 考生信息回溯
        List<ExamTaskExamStudentImportResult> result = new ArrayList<>();

        List<String> studentCodeDatasource = examStudentImportDtoList.stream().map(ExamStudentImportDto::getStudentCode).distinct().collect(Collectors.toList());
        // 导入的excel创建或查询的学生集合
        List<BasicExamStudentResult> basicStudentResultDatasource = basicStudentService.basicStudentList(requestUser.getSchoolId(), null, null, null, null, studentCodeDatasource);

        // 考试对象键(考点、考场)
        List<Map<String, String>> examObjectKeyList = examStudentImportDtoList.stream().flatMap(e -> {
            Map<String, String> key = new HashMap<>();
            key.put("examPlace", e.getExamPlace());
            key.put("examRoom", e.getExamRoom());
            return Stream.of(key);
        }).distinct().collect(Collectors.toList());

        for (Map<String, String> key : examObjectKeyList) {
            // 考点
            String examPlace = key.get("examPlace");
            // 考场
            String examRoom = key.get("examRoom");

            // 该考试对象考生考号集合
            List<String> studentCodeList = examStudentImportDtoList.stream()
                    .filter(e -> examPlace.equals(e.getExamPlace()) && examRoom.equals(e.getExamRoom()))
                    .map(ExamStudentImportDto::getStudentCode)
                    .distinct()
                    .collect(Collectors.toList());

            List<ExamTaskStudentObjectResult> examTaskStudentObjectResultList = new ArrayList<>();
            List<BasicExamStudentResult> basicStudentResultList = basicStudentResultDatasource.stream().filter(e -> studentCodeList.contains(e.getStudentCode())).collect(Collectors.toList());
            List<Long> basicClazzIdList = basicStudentResultList.stream().map(BasicExamStudentResult::getClazzId).distinct().collect(Collectors.toList());
            for (Long basicClazzId : basicClazzIdList) {
                List<BasicExamStudentResult> basicStudentClazzList = basicStudentResultList.stream()
                        .filter(e -> basicClazzId.equals(e.getClazzId())).collect(Collectors.toList());
                List<StudentInfo> studentInfoList = basicStudentClazzList.stream().flatMap(e -> {
                    StudentInfo studentInfo = new StudentInfo();
                    studentInfo.setStudentId(e.getId());
                    studentInfo.setStudentCode(e.getStudentCode());
                    studentInfo.setStudentName(e.getStudentName());
                    studentInfo.setCollegeName(e.getCollegeName());
                    studentInfo.setMajorName(e.getMajorName());
                    studentInfo.setBasicClazzId(e.getClazzId());
                    studentInfo.setBasicClazzName(e.getClazz());
                    return Stream.of(studentInfo);
                }).collect(Collectors.toList());
                ExamTaskStudentObjectResult examTaskStudentObjectResult = new ExamTaskStudentObjectResult();
                examTaskStudentObjectResult.setClazzId(basicClazzId);
                examTaskStudentObjectResult.setClazzName(basicClazzService.getById(basicClazzId).getClazzName());
                examTaskStudentObjectResult.setStudentInfoList(studentInfoList);
                examTaskStudentObjectResultList.add(examTaskStudentObjectResult);
            }


            ExamTaskExamStudentImportResult examTaskExamStudentImportResult = new ExamTaskExamStudentImportResult();
            examTaskExamStudentImportResult.setExamRoom(examRoom);
            if (SystemConstant.strNotNull(examPlace)) {
                examTaskExamStudentImportResult.setExamPlace(examPlace);
            }
            examTaskExamStudentImportResult.setExamTaskStudentObjectResultList(examTaskStudentObjectResultList);
            result.add(examTaskExamStudentImportResult);
        }
        return result;
    }

    @Override
    public void delete(Long id) {
        // 命题任务生成，命题老师未提交前允许删除
        ExamTask examTask = this.getById(id);
        if (examTask != null && examTask.getStatus().equals(ExamStatusEnum.SUBMIT)) {
            throw ExceptionResultEnum.ERROR.exception("命题老师已提交，不能删除");
        }
        this.removeByExamTaskId(id);
    }

    @Transactional
    @Override
    public void applyDelete(Long id) {
        ExamTask examTask = this.getById(id);
        QueryWrapper<TFFlowApprove> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TFFlowApprove::getFlowId, examTask.getFlowId());
        TFFlowApprove tfFlowApprove = tfFlowApproveService.getOne(queryWrapper);
        //审核状态
        if (tfFlowApprove != null && !tfFlowApprove.getStatus().equals(FlowStatusEnum.START) && !tfFlowApprove.getStatus().equals(FlowStatusEnum.REJECT) && !tfFlowApprove.getStatus().equals(FlowStatusEnum.CANCEL)) {
            throw ExceptionResultEnum.ERROR.exception(tfFlowApprove.getStatus().getTitle() + "状态无法删除");
        }
        // 删除命题任务及试卷信息
        this.removeByExamTaskId(id);
        // 删除考场信息
        examDetailService.deleteByExamIdAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), examTask.getPaperNumber());
    }

    @Override
    public boolean savePreviewLog(Long examTaskId, String paperType, String type) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        ExamTask examTask = this.getById(examTaskId);
        if (examTask != null) {
            BasicOperationLog basicOperationLog = new BasicOperationLog();
            basicOperationLog.setId(SystemConstant.getDbUuid());
            basicOperationLog.setSchoolId(sysUser.getSchoolId());
            BasicExam basicExam = basicExamService.getById(examTask.getExamId());
            String examName = basicExam == null ? "-" : basicExam.getName();
            String courseName = examTask.getCourseName() + "(" + examTask.getCourseCode() + ")";
            basicOperationLog.setDetail(String.format("考试[%s]，课程[%s]，试卷编号[%s]，题卡/试卷[%s]，卷型[%s]", examName, courseName, examTask.getPaperNumber(), type.equals("paper") ? "试卷" : "题卡", paperType));
            basicOperationLog.setOperationType(OperationTypeEnum.PREVIEW);
            basicOperationLog.setPrivilegeId(ServletUtil.getCurrentPrivilegeId());
            basicOperationLog.setRunStatus("成功");
            basicOperationLog.setUserId(sysUser.getId());
            basicOperationLog.setIp(ServletUtil.getIP());
            basicOperationLog.setCreateTime(System.currentTimeMillis());
            basicOperationLogService.save(basicOperationLog);
        }
        return true;
    }

    @Override
    public List<String> listPaperNumber(String param, List<Long> printPlanIdList) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        SysOrg sysOrg = sysOrgService.getById(sysUser.getOrgId());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        if (printPlanIdList.size() == 0) {
            // 查询命题任务中的试卷编号
            QueryWrapper<ExamTask> queryWrapperExamTask = new QueryWrapper<>();
            queryWrapperExamTask.lambda().eq(ExamTask::getSchoolId, schoolId);
            if (sysOrg != null && !OrgTypeEnum.PRINTING_HOUSE.equals(sysOrg.getType()) && orgIds.size() > 0) {
                queryWrapperExamTask.lambda().in(ExamTask::getOrgId, orgIds);
            }
            List<String> list = new ArrayList<>();
            if (StringUtils.isNotBlank(param)) {
                queryWrapperExamTask.lambda().and(i -> i.like(ExamTask::getCourseCode, SystemConstant.translateSpecificSign(param)).or().like(ExamTask::getCourseName, SystemConstant.translateSpecificSign(param)));
            }
            List<ExamTask> examTasks = this.list(queryWrapperExamTask);
            if (examTasks != null && examTasks.size() > 0) {
                list = examTasks.stream().map(ExamTask::getPaperNumber).distinct().collect(Collectors.toList());
            }

            // 查询考务-科目中的试卷编号
            QueryWrapper<ExamDetailCourse> queryWrapperExamDetailCourse = new QueryWrapper<>();
            queryWrapperExamDetailCourse.lambda().eq(ExamDetailCourse::getSchoolId, schoolId);
            List<String> list2 = new ArrayList<>();
            if (StringUtils.isNotBlank(param)) {
                queryWrapperExamDetailCourse.lambda().and(i -> i.like(ExamDetailCourse::getCourseCode, SystemConstant.translateSpecificSign(param))
                        .or().like(ExamDetailCourse::getCourseName, SystemConstant.translateSpecificSign(param)));
            }
            List<ExamDetailCourse> ExamDetailCourses = examDetailCourseService.list(queryWrapperExamDetailCourse);
            if (ExamDetailCourses != null && ExamDetailCourses.size() > 0) {
                list2 = ExamDetailCourses.stream().map(ExamDetailCourse::getPaperNumber).distinct().collect(Collectors.toList());
            }
            list.addAll(list2);
            return list.stream().distinct().collect(Collectors.toList());
        } else {
            return examDetailCourseService.listPaperNumberByPrintPlanId(param, printPlanIdList, orgIds);
        }
    }

    @Override
    public ExamTask getByExamIdAndCourseCodeAndPaperNumber(Long examId, String courseCode, String paperNumber) {
        QueryWrapper<ExamTask> queryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<ExamTask> lambdaQueryWrapper = queryWrapper.lambda();
        lambdaQueryWrapper.eq(ExamTask::getExamId, examId);
        if (StringUtils.isNotBlank(courseCode)) {
            lambdaQueryWrapper.eq(ExamTask::getCourseCode, courseCode);
        }
        lambdaQueryWrapper.eq(ExamTask::getPaperNumber, paperNumber);
        return this.getOne(queryWrapper);
    }

    @Override
    public IPage<MakeupExamTaskDto> listMakeupExamTask(Long printPlanId, Long semesterId, Long examId, Long collegeId, String courseCode, String paperNumber, String userName, Integer pageNumber, Integer pageSize) {
        if (semesterId == null) {
            throw ExceptionResultEnum.ERROR.exception("请选择学期");
        }
        if (examId == null) {
            throw ExceptionResultEnum.ERROR.exception("请选择考试");
        }
        Page<MakeupExamTaskDto> page = new Page<>(pageNumber, pageSize);
        return this.baseMapper.listMakeupExamTask(page, printPlanId, semesterId, examId, collegeId, courseCode, paperNumber, userName);
    }

    @Override
    public void saveMakeupTask(MakeupExamTaskTotalParam makeupExamTaskTotalParam) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        ExamPrintPlan examPrintPlan = examPrintPlanService.getById(makeupExamTaskTotalParam.getPrintPlanId());

        for (MakeupExamTaskParam makeupExamTaskParam : makeupExamTaskTotalParam.getList()) {
            ExamDetail examDetail;
            if (makeupExamTaskParam.getExamDetailId() == null) {
                // 课程是否在其它补考计划已创建任务
                int count = examDetailService.countMakeupUsedByExamIdAndPaperNumber(makeupExamTaskParam.getExamId(), makeupExamTaskParam.getPaperNumber());
                if (count > 0) {
                    throw ExceptionResultEnum.ERROR.exception("试卷编号[" + makeupExamTaskParam.getPaperNumber() + "]已在其它计划中提交");
                }

                examDetail = new ExamDetail();
                examDetail.setId(SystemConstant.getDbUuid());
                examDetail.setPackageCode("1" + redisCounterUtil.getCounter(schoolId, 6, "packageCode"));
                examDetail.setSchoolId(schoolId);
                examDetail.setOrgId(sysUser.getOrgId());
                examDetail.setPrintPlanId(examPrintPlan.getId());
                examDetail.setPrintPlanName(examPrintPlan.getName());
                examDetail.setExamId(makeupExamTaskParam.getExamId());
                examDetail.setStatus(ExamDetailStatusEnum.NEW);
                examDetail.setExamStartTime(DateDisposeUtils.parseDate(DateDisposeUtils.getDate() + " 00:00:00").getTime());
                examDetail.setExamEndTime(DateDisposeUtils.parseDate(DateDisposeUtils.getDate() + " 23:59:00").getTime());
                examDetail.setTotalSubjects(makeupExamTaskParam.getTotalSubjects());
                examDetail.setExamDataSource(ExamDataSourceEnum.EXAM_TASK);
                examDetail.setNormal(true);
                examDetail.setBackupCount(makeupExamTaskParam.getBackupCount());
                examDetail.setCreateId(sysUser.getId());
                examDetail.setCreateTime(System.currentTimeMillis());
                examDetailService.save(examDetail);

                ExamDetailCourse examDetailCourse = new ExamDetailCourse();
                examDetailCourse.setId(SystemConstant.getDbUuid());
                examDetailCourse.setSchoolId(schoolId);
                examDetailCourse.setExamDetailId(examDetail.getId());
                examDetailCourse.setCourseCode(makeupExamTaskParam.getCourseCode());
                examDetailCourse.setCourseName(makeupExamTaskParam.getCourseName());
                examDetailCourse.setPaperNumber(makeupExamTaskParam.getPaperNumber());
                examDetailCourse.setTotalSubjects(makeupExamTaskParam.getTotalSubjects());
                examDetailCourse.setCreateId(sysUser.getId());
                examDetailCourseService.save(examDetailCourse);
            } else {
                examDetail = examDetailService.getById(makeupExamTaskParam.getExamDetailId());
                if (Objects.nonNull(examDetail.getTotalSubjects()) && Objects.nonNull(makeupExamTaskParam.getTotalSubjects()) && examDetail.getTotalSubjects().intValue() != makeupExamTaskParam.getTotalSubjects().intValue()) {
                    examDetail.setTotalSubjects(makeupExamTaskParam.getTotalSubjects());
                    examDetail.setStatus(ExamDetailStatusEnum.NEW);
                    examDetailService.updateById(examDetail);

                    UpdateWrapper<ExamDetailCourse> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().set(ExamDetailCourse::getTotalSubjects, makeupExamTaskParam.getTotalSubjects())
                            .eq(ExamDetailCourse::getExamDetailId, examDetail.getId());
                    examDetailCourseService.update(updateWrapper);
                }
            }

            // 生成pdf
            printCommonService.checkDataMakeup(schoolId, examDetail, makeupExamTaskParam.getCourseCode(), makeupExamTaskParam.getPaperNumber(), sysUser);
        }
    }

    @Override
    public IPage<TikuPaperInfo> pageTikuPaper(String courseCode, String paperName, String account, Integer pageNumber, Integer pageSize) {
        if (StringUtils.isBlank(courseCode)) {
            throw ExceptionResultEnum.ERROR.exception("请选择课程");
        }
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        return tikuUtils.pagePaperInfo(schoolId, courseCode, paperName, account, pageNumber, pageSize);
    }

    @Override
    public Map<String, Object> getTikuPaperData(Long examId, Long paperId, String uuid) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Map<String, Object> map = new HashMap<>();
        try {
            File paperDataFile = tikuUtils.getTikuPaperData(schoolId, paperId);
            ExamTaskPaperData examTaskPaperData = saveTikuPaperData(examId, paperId, uuid, paperDataFile);
            map.put("paperJson", examTaskPaperData.getPaperJson());
            map.put("answerJson", examTaskPaperData.getAnswerJson());
            map.put("attachmentId", String.valueOf(examTaskPaperData.getPaperPdfAttachmentId()));
            return map;
        } catch (Exception e) {
            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
        }
    }

    @Transactional
    @Override
    public List<ExamDetail> matchBasicExamStudent(BasicExam basicExam, ExamTask examTask, ExamTaskDetail examTaskDetail, SysUser sysUser) {
        List<BasicExamStudent> basicExamStudentList = basicExamStudentService.listByExamIdAndPaperNumber(examTask.getExamId(), examTask.getPaperNumber());
        if (CollectionUtils.isEmpty(basicExamStudentList)) {
            return null;
        }
        // 创建印刷计划
        QueryWrapper<ExamPrintPlan> printPlanQueryWrapper = new QueryWrapper<>();
        printPlanQueryWrapper.lambda().eq(ExamPrintPlan::getExamId, basicExam.getId()).eq(ExamPrintPlan::getName, examTask.getCourseName() + examTask.getPaperNumber());
        ExamPrintPlan examPrintPlan = examPrintPlanService.getOne(printPlanQueryWrapper);
        if (examPrintPlan != null) {
            return null;
        }

        examPrintPlan = new ExamPrintPlan();
        examPrintPlan.insertInfo(sysUser.getId());
        examPrintPlan.setSchoolId(sysUser.getSchoolId());
        examPrintPlan.setExamId(examTask.getExamId());
        examPrintPlan.setOrgId(sysUser.getOrgId());
        examPrintPlan.setName(examTask.getCourseName() + examTask.getPaperNumber());
        examPrintPlan.setExamStartTime(System.currentTimeMillis());
        examPrintPlan.setExamEndTime(System.currentTimeMillis());
        BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamId(basicExam.getId());
        List<String> stringList = JSONObject.parseArray(basicPrintConfig.getPrintContent(), String.class);
        if (!stringList.contains("PAPER")) {
            List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
            long count = paperInfoVoList.stream().filter(m -> Objects.isNull(m.getAttachmentId())).count();
            if (count == 0) {
                stringList.add("PAPER");
            }
        }
        examPrintPlan.setPrintContent(String.join(",", stringList));
        examPrintPlan.setBackupMethod(BackupMethodEnum.valueOf(basicPrintConfig.getBackupMethod()));
        examPrintPlan.setBackupCount(basicPrintConfig.getBackupCount());
        examPrintPlan.setDrawRule(basicPrintConfig.getDrawRule());
        examPrintPlan.setVariableContent(basicPrintConfig.getVariableContent());
        examPrintPlan.setOrdinaryContent(basicPrintConfig.getOrdinaryContent());
        examPrintPlan.setStatus(PrintPlanStatusEnum.READY);
        examPrintPlanService.save(examPrintPlan);

        List<Map<String, Object>> examDetailKeyList = basicExamStudentList.stream().flatMap(e -> {
            Long schoolIdId = e.getSchoolId();
            Map<String, Object> map = new HashMap<>();
            map.put("schoolId", schoolIdId);
            map.put("examPlace", e.getExamPlace());
            map.put("examRoom", e.getExamRoom());
            map.put("examStartTime", e.getExamStartTime());
            map.put("examEndTime", e.getExamEndTime());
            return Stream.of(map);
        }).distinct().collect(Collectors.toList());

        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"));
            Long examStartTime = Long.valueOf(String.valueOf(map.get("examStartTime")));
            Long examEndTime = Long.valueOf(String.valueOf(map.get("examEndTime")));

            List<BasicExamStudent> examDetailStudentList = basicExamStudentList.stream().filter(e -> e.getExamPlace().equals(examPlace) &&
                            e.getExamRoom().equals(examRoom) && e.getExamStartTime().equals(examStartTime) && e.getExamEndTime().equals(examEndTime))
                    .collect(Collectors.toList());

            ExamDetail examDetail = new ExamDetail();
            examDetail.setId(SystemConstant.getDbUuid());
            examDetail.setPackageCode("1" + redisCounterUtil.getCounter(schoolId, 6, "packageCode"));
            examDetail.setSchoolId(schoolId);
            examDetail.setOrgId(sysUser.getOrgId());
            examDetail.setPrintPlanId(examPrintPlan.getId());
            examDetail.setPrintPlanName(examPrintPlan.getName());
            examDetail.setExamId(examPrintPlan.getExamId());
            examDetail.setExamPlace(examPlace);
            examDetail.setExamRoom(examRoom);
            examDetail.setStatus(ExamDetailStatusEnum.NEW);
            examDetail.setTotalSubjects(examDetailStudentList.size());
            examDetail.setExamStartTime(examStartTime);
            examDetail.setExamEndTime(examEndTime);
            examDetail.setExamDataSource(ExamDataSourceEnum.FILE_IMPORT);
            examDetail.setNormal(true);
            examDetail.setCreateId(sysUser.getId());
            examDetail.setBackupCount(examPrintPlan.getBackupCount());
            examDetailService.save(examDetail);

            //组装exam_detail_course数据
            List<Map<String, Object>> examDetailCourseKeyList = examDetailStudentList.stream().flatMap(e -> {
                Map<String, Object> examDetailCourseMap = new HashMap<>();
                examDetailCourseMap.put("courseCode", e.getCourseCode());
                examDetailCourseMap.put("paperNumber", e.getPaperNumber());
                return Stream.of(examDetailCourseMap);
            }).distinct().collect(Collectors.toList());


            for (Map<String, Object> examDetailCourseMap : examDetailCourseKeyList) {
                String courseCode = String.valueOf(examDetailCourseMap.get("courseCode"));
                String paperNumber = String.valueOf(examDetailCourseMap.get("paperNumber"));

                // 获取该科目下的考生人数
                List<BasicExamStudent> examDetailCourseStudentList = examDetailStudentList.stream().filter(e -> e.getCourseCode().equals(courseCode) &&
                        e.getPaperNumber().equals(paperNumber)).collect(Collectors.toList());

                ExamDetailCourse examDetailCourse = new ExamDetailCourse();
                examDetailCourse.setId(SystemConstant.getDbUuid());
                examDetailCourse.setSchoolId(schoolId);
                examDetailCourse.setExamDetailId(examDetail.getId());
                examDetailCourse.setCourseCode(courseCode);
                examDetailCourse.setCourseName(examTask.getCourseName());
                examDetailCourse.setPaperNumber(paperNumber);
                examDetailCourse.setCoursePaperId(courseCode.concat(examTask.getSequence()));
                examDetailCourse.setTotalSubjects(examDetailCourseStudentList.size());
                examDetailCourse.setCreateId(sysUser.getId());
                Set<Long> teachClazzIds = examDetailCourseStudentList.stream().filter(m -> m.getClazzId() != null).map(BasicExamStudent::getClazzId).collect(Collectors.toSet());
                List<BasicTeachClazz> basicTeachClazzes = basicTeachClazzService.listByIds(teachClazzIds);
                if (CollectionUtils.isNotEmpty(basicTeachClazzes)) {
                    examDetailCourse.setClazzName(basicTeachClazzes.stream().map(BasicTeachClazz::getClazzName).collect(Collectors.joining(",")));
                }
                examDetailCourseService.save(examDetailCourse);

                //组装exam_student数据
                List<ExamStudent> examStudentList = new ArrayList<>();
                AtomicInteger atomicInteger = new AtomicInteger(1);
                for (BasicExamStudent basicExamStudent : examDetailCourseStudentList) {
                    ExamStudent examStudent = new ExamStudent();
                    examStudent.setId(SystemConstant.getDbUuid());
                    examStudent.setSchoolId(schoolId);
                    examStudent.setOrgId(sysUser.getOrgId());
                    examStudent.setExamId(examTask.getExamId());
                    examStudent.setExamDetailCourseId(examDetailCourse.getId());
                    examStudent.setPaperNumber(paperNumber);
                    examStudent.setCoursePaperId(examDetailCourse.getCoursePaperId());
                    examStudent.setStudentCode(basicExamStudent.getStudentCode());
                    examStudent.setStudentName(basicExamStudent.getStudentName());
                    examStudent.setSiteNumber(String.valueOf(atomicInteger.getAndIncrement()));
                    examStudent.setCollegeName(basicExamStudent.getCollege());
                    examStudent.setMajorName(basicExamStudent.getMajor());
                    examStudent.setTeacherId(basicExamStudent.getTeacherId());
                    BasicTeachClazz basicTeachClazz = basicTeachClazzService.getById(basicExamStudent.getClazzId());
                    examStudent.setTeachClazzId(basicExamStudent.getClazzId());
                    examStudent.setTeachClazzName(basicTeachClazz != null ? basicTeachClazz.getClazzName() : null);
                    examStudent.setCreateId(sysUser.getId());
                    examStudentList.add(examStudent);
                }
                examStudentService.saveBatch(examStudentList);
                examDetailList.add(examDetail);
            }
        }
        return examDetailList;
    }

    private ExamTaskPaperData saveTikuPaperData(Long examId, Long paperId, String uuid, File zipFile) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        String zipDestPath = null;
        try {
            zipDestPath = zipFile.getParent() + File.separator + System.nanoTime();
            Zip4jUtil.unzip(zipFile.getPath(), zipDestPath);

            ExamTaskPaperData examTaskPaperData = examTaskPaperDataService.selectByMultiId(new ExamTaskPaperData(examId, uuid, paperId));
            if (examTaskPaperData == null) {
                examTaskPaperData = new ExamTaskPaperData();
                examTaskPaperData.setExamId(examId);
                // 唯一码，命题任务提交时，根据唯一码反向更新真实的试卷编号
                examTaskPaperData.setPaperNumber(uuid);
                examTaskPaperData.setUuid(uuid);
                examTaskPaperData.setPaperId(paperId);
            }
            // data.zip
            String zipPdfName = PdfUtil.md5FileName(examTaskPaperData.getUuid() + "data" + paperId) + SystemConstant.ZIP_PREFIX;
            FilePathVo filePathVoZip = fileUploadService.uploadFile(zipFile, UploadFileEnum.FILE, zipPdfName);
            examTaskPaperData.setDataPacketPath(JSON.toJSONString(filePathVoZip));
            // paper.json
            String paperJson = FileUtils.readFileToString(new File(zipDestPath + File.separator + "paper.json"), StandardCharsets.UTF_8);
            examTaskPaperData.setPaperJson(paperJson);
            // answer.json
            String answerJson = FileUtils.readFileToString(new File(zipDestPath + File.separator + "answer.json"), StandardCharsets.UTF_8);
            examTaskPaperData.setAnswerJson(answerJson);
            // paper.pdf
            String paperPdfName = PdfUtil.md5FileName(examTaskPaperData.getUuid() + "paper" + paperId) + SystemConstant.PDF_PREFIX;
            File paperPdfFile = new File(zipDestPath + File.separator + "paper.pdf");
            FilePathVo filePathVoPaper = fileUploadService.uploadFile(paperPdfFile, UploadFileEnum.PDF, paperPdfName);
            BasicAttachment paperPdfBasicAttachment = new BasicAttachment(JSON.toJSONString(filePathVoPaper), paperPdfName, SystemConstant.PDF_PREFIX, new BigDecimal(paperPdfFile.length()), filePathVoPaper.getMd5(), sysUser.getId());
            basicAttachmentService.save(paperPdfBasicAttachment);
            examTaskPaperData.setPaperPdfAttachmentId(paperPdfBasicAttachment.getId());
            // answer.pdf
            String answerPdfName = PdfUtil.md5FileName(examTaskPaperData.getUuid() + "answer" + paperId) + SystemConstant.PDF_PREFIX;
            File answerPdfFile = new File(zipDestPath + File.separator + "answer.pdf");
            FilePathVo filePathVoAnswer = fileUploadService.uploadFile(answerPdfFile, UploadFileEnum.PDF, answerPdfName);
            BasicAttachment answerBasicAttachment = new BasicAttachment(JSON.toJSONString(filePathVoAnswer), answerPdfName, SystemConstant.PDF_PREFIX, new BigDecimal(answerPdfFile.length()), filePathVoAnswer.getMd5(), sysUser.getId());
            basicAttachmentService.save(answerBasicAttachment);
            examTaskPaperData.setAnswerPdfAttachmentId(answerBasicAttachment.getId());
            examTaskPaperDataService.saveOrUpdateByMultiId(examTaskPaperData);
            return examTaskPaperData;
        } catch (Exception e) {
            throw ExceptionResultEnum.ERROR.exception("获取试卷包数据失败");
        } finally {
            if (zipFile.exists()) {
                FileUtil.deleteFile(zipFile);
            }
            if (zipDestPath != null) {
                FileUtil.deleteDirectory(zipDestPath);
            }
        }

    }

    /**
     * 简单校验提交参数
     *
     * @param examTaskDetail
     */
    void validSubmitParam(ExamTaskDetail examTaskDetail, String printContent) {
        if (StringUtils.isBlank(examTaskDetail.getPaperType())) {
            throw ExceptionResultEnum.ERROR.exception("没有设置卷型");
        }

        List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
        for (PaperInfoVo paperInfoVo : paperInfoVoList) {
            // 校验试卷必须绑定题卡
            Long cardId = Long.valueOf(paperInfoVo.getCardId());
            if (Objects.isNull(cardId)) {
                throw ExceptionResultEnum.ERROR.exception("没有绑定题卡");
            }
            // 校验题卡是否提交
            ExamCard examCard = examCardService.getById(cardId);
            if (!ExamCardStatusEnum.SUBMIT.name().equals(examCard.getStatus().name())) {
                throw ExceptionResultEnum.ERROR.exception("绑定的题卡未提交");
            }
        }

        String[] paperTypes = examTaskDetail.getPaperType().split(",");
        if (paperTypes.length - examTaskDetail.getDrawCount() < 0) {
            throw ExceptionResultEnum.ERROR.exception("单次抽卷数量不能大于卷型数量");
        }

        // 是否强制包含试卷
        // 未上传试卷的类型个数
        long count = paperInfoVoList.stream().filter(m -> Objects.isNull(m.getAttachmentId())).count();
        if (printContent.contains("PAPER")) {
            if (CollectionUtils.isEmpty(paperInfoVoList)) {
                throw ExceptionResultEnum.ERROR.exception("没有设置卷型");
            }
            if (count > 0) {
                throw ExceptionResultEnum.ERROR.exception("有部分卷型未上传试卷文件");
            }
        } else {
            // count == 0为全部上传，paperAttachmentIds.size() == count 为全部未上传
            if (count > 0 && paperInfoVoList.size() != count) {
                throw ExceptionResultEnum.ERROR.exception("所有卷型的试卷文件必须全部上传或全部不上传");
            }
        }
    }

    /**
     * 根据试卷袋信息构建考试对象信息
     *
     * @param examPackageDetailDatasource 试卷袋数据源
     * @return 考试对象
     */
    private ExamObjectDto buildExamObjectDtoByPackageDatasource(List<ExamPackageDetail> examPackageDetailDatasource) {
        Long examStartTime = null;
        Long examEndTime = null;
        int printTotalCount = 0;
        List<ExamRoomInfo> examRoomInfoList = new ArrayList<>();
        if (examPackageDetailDatasource.size() > 0) {
            Set<Long> examDetailIdSet = examPackageDetailDatasource.stream().map(ExamPackageDetail::getExamDetailId).collect(Collectors.toSet());
            for (Long examDetailId : examDetailIdSet) {
                // 一个考场下
                // 班级
                String classNames = "";
                int studentCount = 0;
                String printCount = "";

                List<ExamPackageDetail> examPackageDetailCell = examPackageDetailDatasource.stream()
                        .filter(e -> examDetailId.equals(e.getExamDetailId()))
                        .collect(Collectors.toList());
                List<String> packageCodeList = examPackageDetailCell.stream().map(ExamPackageDetail::getPackageCode).distinct().collect(Collectors.toList());
                String packageCode = "";
                if (packageCodeList.size() > 0) {
                    packageCode = packageCodeList.get(0);
                }

                List<String> printHourseNameList = examPackageDetailCell.stream().map(ExamPackageDetail::getPrintHouseName).distinct().collect(Collectors.toList());
                String printHourseName = "";
                if (printHourseNameList.size() > 0) {
                    printHourseName = printHourseNameList.get(0);
                }

                List<BackupMethodEnum> backupMethodEnums = examPackageDetailCell.stream().map(ExamPackageDetail::getBackupMethod).distinct().collect(Collectors.toList());
                if (backupMethodEnums.size() != 1 && backupMethodEnums.get(0) != BackupMethodEnum.ROOM) {
                    throw ExceptionResultEnum.ERROR.exception("印刷计划试卷备份方式必须按考场");
                }
                List<Double> backupCountList = examPackageDetailCell.stream().map(ExamPackageDetail::getBackupCount).distinct().collect(Collectors.toList());
                if (backupCountList.size() != 1) {
                    throw ExceptionResultEnum.ERROR.exception("印刷计划试卷备份数量异常");
                }

                // 班级主键','隔开
                List<Long> clazzIds = new ArrayList<>();
                String separator = ",";
                for (ExamPackageDetail examPackageDetail : examPackageDetailCell) {
                    // 处理考试开始结束时间
                    Long cellStartTime = examPackageDetail.getExamStartTime();
                    Long cellEndTime = examPackageDetail.getExamEndTime();
                    if (examStartTime == null || cellStartTime < examStartTime) {
                        examStartTime = cellStartTime;
                    }
                    if (examEndTime == null || cellEndTime > examEndTime) {
                        examEndTime = cellEndTime;
                    }
                    // 处理班级和数量
                    Integer studentCellCount = examPackageDetail.getExamDetailCourseTotalSubjects();
                    studentCount = studentCount + studentCellCount;
                    String clazzIdCell = examPackageDetail.getClazzIds();
                    if (StringUtils.isNotBlank(clazzIdCell)) {
                        for (String s : clazzIdCell.split(separator)) {
                            clazzIds.add(Long.valueOf(s));
                        }
                    }
                }
                // 试卷该考场备份数量
                Integer backupCount = SystemConstant.calcBackupCount(backupCountList.get(0), studentCount, 1);
                printCount = studentCount + " + " + backupCount;
                printTotalCount = printTotalCount + studentCount + backupCount;

                // 组装examRoom
                ExamRoomInfo examRoomInfo = new ExamRoomInfo();
                examRoomInfo.setPackageCode(packageCode);
                if (!CollectionUtils.isEmpty(clazzIds)) {
                    classNames = examDetailService.findClazzNamesByClazzIds(clazzIds, separator);
//                    examRoomInfo.setCampusNames(basicCampusService.findCampusNamesByClazzIdList(clazzIds, ","));
                }
                examRoomInfo.setClazzNames(classNames);
                examRoomInfo.setStudentCount(studentCount);
                examRoomInfo.setPrintCount(printCount);
                examRoomInfo.setPrintHouseName(printHourseName);
                examRoomInfoList.add(examRoomInfo);
            }
        }
        ExamObjectDto examObjectDto = new ExamObjectDto();
        examObjectDto.setExamEndTime(examEndTime);
        examObjectDto.setExamStartTime(examStartTime);
        examObjectDto.setExamRoomInfoList(examRoomInfoList);
        examObjectDto.setPrintTotalCount(printTotalCount);
        return examObjectDto;
    }
}
