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

import com.alibaba.fastjson.JSONObject;
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.params.*;
import com.qmth.distributed.print.business.bean.result.WorkResult;
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.CreatePdfUtil;
import com.qmth.distributed.print.business.util.HtmlToPdfUtil;
import com.qmth.teachcloud.common.bean.dto.BlurryUserDto;
import com.qmth.teachcloud.common.bean.dto.MqDto;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.*;
import com.qmth.teachcloud.common.enums.*;
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.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.CollectionUtils;
import org.springframework.util.FileCopyUtils;
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.util.*;
import java.util.stream.Collectors;

/**
 * <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 ExamTaskTempService examTaskTempService;

    @Resource
    private BasicExamRuleService basicExamRuleService;

    @Resource
    private ExamTaskReviewLogService examTaskReviewLogService;

    @Resource
    private ExamTaskDetailService examTaskDetailService;

    @Resource
    private BasicAttachmentService basicAttachmentService;

    @Resource
    @Lazy
    private PrintCommonService printCommonService;

    @Resource
    ExamTaskMapper examTaskMapper;

    @Resource
    private BasicCourseService basicCourseService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Resource
    private ExamCardService examCardService;

    @Resource
    private ExamCardDetailService examCardDetailService;

    @Resource
    private ExamDetailCourseService examDetailCourseService;

    @Resource
    private ExamDetailService examDetailService;

    @Resource
    TBTaskService tbTaskService;

    @Resource
    private ExamTaskPaperLogService examTaskPaperLogService;

    @Resource
    private SysConfigService sysConfigService;

    @Resource
    private BasicMessageService basicMessageService;

    @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
    BasicCampusService basicCampusService;

    @Resource
    ExamStudentService examStudentService;

    @Resource
    TFFlowJoinService tfFlowJoinService;

    @Resource
    CreatePdfUtil createPdfUtil;

    @Resource
    BasicPrintConfigService basicPrintConfigService;

    @Resource
    TFFlowLogService tfFlowLogService;

    @Resource
    CommonCacheService commonCacheService;

    @Resource
    TFCustomFlowService tfCustomFlowService;

    @Resource
    BasicExamService basicExamService;

    @Resource
    BasicSemesterService basicSemesterService;

    @Override
    public List<ExamTask> listByCourseCode(Long schoolId, String code) {
        QueryWrapper<ExamTask> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getCourseCode, code);
        return this.list(queryWrapper);
    }

    @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);
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        Page<ExamTaskDto> page = new Page<>(pageNumber, pageSize);
        IPage<ExamTaskDto> examTaskDtoIPage = this.baseMapper.listPage(page, schoolId, semesterId, examId, enable, status, cardRuleId, courseCode, paperNumber, startTime, endTime, orgIds, userName, createName);
        for (ExamTaskDto record : examTaskDtoIPage.getRecords()) {
            // 判断审批表状态
            String cellPaperNumber = record.getPaperNumber();
            List<PrintPlanStatusEnum> printPlanStatusEnumList = new ArrayList<>();
            printPlanStatusEnumList.add(PrintPlanStatusEnum.PRINTING);
            printPlanStatusEnumList.add(PrintPlanStatusEnum.PRINT_FINISH);
            List<ExamPackageDetail> examPackageDetailDatasource = this.baseMapper.findExamPackageDetailByPaperNumber(schoolId, examId, record.getCourseCode(), cellPaperNumber, printPlanStatusEnumList);
            record.setApproveFormStatus(examPackageDetailDatasource.size() > 0);
        }
        return examTaskDtoIPage;
    }

    @Override
    public List<String> listPaperNumber(String param, List<Long> printPlanIdList) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        if (printPlanIdList.size() == 0) {
            // 查询命题任务中的试卷编号
            QueryWrapper<ExamTask> queryWrapperExamTask = new QueryWrapper<>();
            queryWrapperExamTask.lambda().eq(ExamTask::getSchoolId, schoolId);
            if (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);
            if (orgIds.size() > 0) {
                queryWrapperExamDetailCourse.lambda().in(ExamDetailCourse::getOrgId, orgIds);
            }
            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 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);
    }

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

        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);
            }
        }
        // 更改命题老师
        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);
                    map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.NEW);
                    tfFlowJoinService.saveOrUpdate(map);
                } else {
                    flowId = task.getFlowId();
                }
            }
        }

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

        return this.update(updateWrapper);
    }

    @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);
    }

    @Deprecated
    @Override
    public ExamTask saveExamTask(ExamTask examTask) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        examTask.setSchoolId(schoolId);
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        if (StringUtils.isNotBlank(examTask.getPaperNumber())) {
            QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
            taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getPaperNumber, examTask.getPaperNumber());
            ExamTask task = this.getOne(taskQueryWrapper);
            if (task != null) {
                throw ExceptionResultEnum.ERROR.exception("试卷编号已存在");
            }
        } else {
            // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
            String paperNumber = printCommonService.createPaperNumber(schoolId);
            examTask.setPaperNumber(paperNumber);
        }

        QueryWrapper<BasicExamRule> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(BasicExamRule::getSchoolId, examTask.getSchoolId());
        BasicExamRule basicExamRule = basicExamRuleService.getOne(queryWrapper);
        if (basicExamRule == null) {
            throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
        }
//        if (examTask.getUserId() == null) {
//            examTask.setStatus(ExamStatusEnum.NEW);
//        } else {
//            examTask.setStatus(ExamStatusEnum.READY);
//        }
        examTask.setEnable(true);
        examTask.insertInfo(sysUser.getId());
        examTask.setReview(basicExamRule.getReview());
        this.save(examTask);
        return examTask;
    }

    /**
     * 保存命题任务新
     *
     * @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.getByExamIdAndCourseCode(examId, courseCode);
            examTask.setExamId(examId);
            examTask.setCardRuleId(basicPrintConfig.getCardRuleId());
            examTask.setSchoolId(schoolId);
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            if (StringUtils.isNotBlank(examTask.getPaperNumber())) {
                QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getPaperNumber, examTask.getPaperNumber());
                ExamTask task = this.getOne(taskQueryWrapper);
                if (task != null) {
                    throw ExceptionResultEnum.ERROR.exception("试卷编号已存在");
                }
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                String paperNumber = printCommonService.createPaperNumber(schoolId);
                examTask.setPaperNumber(paperNumber);
            }
            examTask.setPaperNumberId(SystemConstant.getDbUuid());

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

            QueryWrapper<BasicExamRule> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(BasicExamRule::getSchoolId, examTask.getSchoolId());
            BasicExamRule basicExamRule = basicExamRuleService.getOne(queryWrapper);
            if (basicExamRule == null) {
                throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
            }
            examTask.insertInfo(sysUser.getId());
            examTask.updateInfo(sysUser.getId());
            examTask.setReview(basicExamRule.getReview());
            // 取课程所在机构
            Long teachingRoomId = basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, courseCode);
            examTask.setOrgId(teachingRoomId);
            examTask.setTeachingRoomId(teachingRoomId);
            if (examTask.getUserId() == null) {
                examTask.setStatus(ExamStatusEnum.DRAFT);
            } else if (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 (!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 ExamTaskImportDto importFile(MultipartFile file) throws IOException, NoSuchFieldException {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamTaskTempDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            Map<String, String> courseCodePaperNumberMap = new HashMap<>();
            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++) {
                    ExamTaskTempDto userImportDto = (ExamTaskTempDto) examTaskTempList.get(y);
                    if (StringUtils.isBlank(userImportDto.getCourseCode())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
                    }
                    if (StringUtils.isBlank(userImportDto.getCourseName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
                    }

                    // 课程是否存在并且与名称对应
                    QueryWrapper<BasicCourse> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda().eq(BasicCourse::getSchoolId, schoolId).eq(BasicCourse::getCode, userImportDto.getCourseCode());
                    BasicCourse course = basicCourseService.getOne(queryWrapper);
                    if (course == null) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]在系统中不存在"));
                    } else if (!course.getName().equals(userImportDto.getCourseName())) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]与系统中不匹配"));
                    }
                    if (courseCodePaperNumberMap.containsKey(userImportDto.getCourseCode())) {
                        String paperNumber = courseCodePaperNumberMap.get(userImportDto.getCourseCode());
                        if (paperNumber != null && paperNumber.equals(userImportDto.getPaperNumber())) {
                            excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]与[试卷编号]不唯一"));
                        }
                    } else {
                        courseCodePaperNumberMap.put(userImportDto.getCourseCode(), userImportDto.getPaperNumber());
                    }

                    // 校验命题老师
                    String excelCourseCode = userImportDto.getCourseCode();
                    String excelTeacherAccount = userImportDto.getUserAccount();
                    String excelTeacherName = userImportDto.getUserName();

                    List<BlurryUserDto> listUser = sysUserService.listUser(excelCourseCode, null);
                    Map<String, String> userMap = listUser.stream().collect(Collectors.toMap(BlurryUserDto::getLoginName, BlurryUserDto::getName));
                    if (userMap == null) {
                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[命题老师账号]不存在"));
                    } else {
                        if (StringUtils.isNotBlank(excelTeacherAccount)) {
                            if (StringUtils.isBlank(excelTeacherName)) {
                                excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[命题老师姓名]不能为空"));
                            } else {
                                if (StringUtils.isBlank(userMap.get(excelTeacherAccount))) {
                                    excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[命题老师账号]不存在"));
                                } else {
                                    if (!excelTeacherName.equals(userMap.get(excelTeacherAccount))) {
                                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[命题老师姓名]与系统不匹配"));
                                    }
                                }
                            }
                        } else {
                            if (StringUtils.isNotBlank(excelTeacherName)) {
                                excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (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;
        });
        return executeImportExamTaskBatch(finalList);
    }

    @Transactional
    ExamTaskImportDto 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());
        ExamTaskImportDto examTaskImportDto = new ExamTaskImportDto();
        examTaskImportDto.setBatchNo(batchNo);
        List<ExamTaskDto> tasks = new ArrayList<>();
        Set<String> paperNumbers = new HashSet<>();
        // 错误信息
        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++) {
                ExamTaskTempDto examTaskTempDto = (ExamTaskTempDto) examTaskTempList.get(y);
                ExamTaskTemp examTaskTemp = gson.fromJson(gson.toJson(examTaskTempDto), ExamTaskTemp.class);
                examTaskTemp.setId(SystemConstant.getDbUuid());
                examTaskTemp.setBatchNo(batchNo);
                examTaskTemp.setSchoolId(schoolId);
                examTaskTemp.setCreateId(sysUser.getId());
                examTaskTemp.setCreateTime(System.currentTimeMillis());
                //保存
                examTaskTempService.save(examTaskTemp);

                if (StringUtils.isNotBlank(examTaskTemp.getPaperNumber())) {
                    if (paperNumbers.contains(examTaskTemp.getPaperNumber())) {
                        // 试卷编号在文件内重复，跳过
                        String error = examTaskTemp.getPaperNumber() + "文件中重复";
                        stringJoiner.add(error);
                        continue;
                    }
                    QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                    taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getPaperNumber, examTaskTemp.getPaperNumber());
                    ExamTask task1 = this.getOne(taskQueryWrapper);
                    if (task1 != null) {
                        // 试卷编号已存在，直接跳过
                        String error = examTaskTemp.getPaperNumber() + "已存在";
                        stringJoiner.add(error);
                        continue;
                    }
                    paperNumbers.add(examTaskTemp.getPaperNumber());
                }

                ExamTaskDto examTaskDto = new ExamTaskDto();
                examTaskDto.setCourseCode(examTaskTemp.getCourseCode());
                examTaskDto.setCourseName(examTaskTemp.getCourseName());
                examTaskDto.setPaperNumber(examTaskTemp.getPaperNumber());
                examTaskDto.setSpecialty(examTaskTemp.getSpecialty());
                List<BlurryUserDto> blurryUserDtoList;

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

//                // 校验命题老师
//                if (StringUtils.isBlank(examTaskTemp.getUserAccount())) {
//                    examTaskDto.setUsers(listUsers(examTaskTemp.getCourseCode(), null));
//                } else {
//                    QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
//                    queryWrapper.lambda().eq(SysUser::getLoginName, examTaskTemp.getUserAccount());
//                    SysUser sysUser1 = sysUserService.getOne(queryWrapper);
//                    if (sysUser1 == null) {
//                        examTaskDto.setUsers(listUsers(examTaskTemp.getCourseCode(), null));
//                    } else {
//                        List<BlurryUserDto> blurryUserDtoList = listUsers(examTaskTemp.getCourseCode(), String.valueOf(sysUser1.getId()));
//
//                        if (blurryUserDtoList.size() == 0){
//                            //如果没有查询到模糊用户数据 可能是excel中命题老师没有该课程权限，则舍弃该用户再次查询该科目-试卷的模糊用户
//                            blurryUserDtoList = listUsers(examTaskTemp.getCourseCode(), null);
//                        }
//                        examTaskDto.setUsers(blurryUserDtoList);
//                    }
//                }
                tasks.add(examTaskDto);
            }
        }

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

    private List<BlurryUserDto> listUsers(String courseCode, String userId) {
        return sysUserService.listUser(courseCode, userId);
    }

    @Override
    public List<ExamTask> saveBatch(ExamTask task) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Long examId = task.getExamId();

        task.setSchoolId(schoolId);
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();

        if (StringUtils.isBlank(task.getBatchNo())) {
            throw ExceptionResultEnum.ERROR.exception("batchNo不能为空");
        }

        QueryWrapper<BasicExamRule> examQueryWrapper = new QueryWrapper<>();
        examQueryWrapper.lambda().eq(BasicExamRule::getSchoolId, schoolId);
        BasicExamRule basicExamRule = basicExamRuleService.getOne(examQueryWrapper);
        if (basicExamRule == null) {
            throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
        }

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

        for (Map<String, String> userMap : userList) {
            String courseCode = userMap.get("courseCode");
            // 配置信息
            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamIdAndCourseCode(examId, courseCode);
            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.setSpecialty(userMap.get("specialty"));
            examTask.setExamId(examId);
            examTask.setCardRuleId(cardRuleId);

            if (StringUtils.isNotBlank(userMap.get("paperNumber"))) {
                if (paperNumbers.contains(userMap.get("paperNumber"))) {
                    // 试卷编号在文件内重复，跳过
                    continue;
                }
                QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).eq(ExamTask::getPaperNumber, userMap.get("paperNumber"));
                ExamTask task1 = this.getOne(taskQueryWrapper);
                if (task1 != null) {
//                    throw ExceptionResultEnum.ERROR.exception("试卷编号已存在");
                    // 试卷编号已存在，直接跳过
                    continue;
                }
                paperNumbers.add(userMap.get("paperNumber"));
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                String paperNumber = printCommonService.createPaperNumber(schoolId);
                userMap.put("paperNumber", paperNumber);

                String sequence = printCommonService.createCourseSequence(schoolId, courseCode);
                userMap.put("sequence", sequence);
            }
            examTask.setPaperNumber(userMap.get("paperNumber"));
            examTask.setSequence(userMap.get("sequence"));
            examTask.setPaperNumberId(SystemConstant.getDbUuid());
            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");
            String specialty = userMap.get("specialty");
            if (specialty != null && specialty.length() > 0 && !specialty.equals("null")) {
                examTask.setSpecialty(specialty);
            }

            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(basicExamRule.getReview());
            list.add(examTask);
        }
        this.saveBatch(list);
        for (ExamTask examTask : list) {
            if (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);
            }
        }
        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();
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        IPage<ExamTaskDto> examTaskDtoIPage = this.baseMapper.listTaskApply(new Page<>(pageNumber, pageSize), schoolId, semesterId, examId, auditStatus, cardRuleId, courseCode, paperNumber, startTime, endTime, sysUser.getId(), orgIds, userName);
        for (ExamTaskDto record : examTaskDtoIPage.getRecords()) {
            if ((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());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        return this.baseMapper.listTaskReviewUnaudited(new Page<>(pageNumber, pageSize), schoolId, semesterId, examId, courseCode, paperNumber, userId, cardRuleId, startTime, endTime, orgIds, 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) throws IOException {
        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("当前状态不能审核");
            }

//            // 更新命题任务状态
//            UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
//            ExamStatusEnum statusEnum;
//            if (taskReviewLog.getReviewStatus().name().equals(ReviewStatusEnum.PASS.name())) {
//                statusEnum = ExamStatusEnum.FINISH;
//            } else {
//                statusEnum = ExamStatusEnum.STAGE;
//            }
//            updateWrapper.lambda().set(ExamTask::getStatus, statusEnum).set(ExamTask::getReviewStatus, taskReviewLog.getReviewStatus()).eq(ExamTask::getId, taskReviewLog.getExamTaskId());
//            this.update(updateWrapper);
//        } else {
//             更新记录表状态
//            taskAfterPass(taskReviewLog.getReviewStatus(), taskReviewLog.getExamTaskId(), examTaskPaperLog, sysUser);
        }
        return true;
    }

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

        ExamStatusEnum statusEnum;
//        if (taskReviewLog.getReviewStatus().name().equals(ReviewStatusEnum.PASS.name())) {
//            statusEnum = ExamStatusEnum.FINISH;
//        } else {
//            statusEnum = ExamStatusEnum.STAGE;
//        }
//        for (Long examTaskId : taskReviewLog.getExamTaskIds()) {
//            ExamTaskPaperLog examTaskPaperLog = examTaskPaperLogService.getByExamTaskIdAndReview(examTaskId, false);
//            if (examTaskPaperLog == null) {
//                // 校验状态，可能被撤回
//                ExamTask examTask = this.getById(examTaskId);
//                if (!ExamStatusEnum.SUBMIT.equals(examTask.getStatus())) {
//                    throw ExceptionResultEnum.ERROR.exception("当前状态不能审核");
//                }
//                // 审核日志
//                taskReviewLog.setOperateId(sysUser.getId());
//                taskReviewLog.setOperateTime(System.currentTimeMillis());
//                taskReviewLog.setExamTaskId(examTaskId);
//                taskReviewLog.setId(SystemConstant.getDbUuid());
//                examTaskReviewLogService.save(taskReviewLog);
//
//                // 更新命题任务状态
//                UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
//                updateWrapper.lambda().set(ExamTask::getStatus, statusEnum).set(ExamTask::getReviewStatus, taskReviewLog.getReviewStatus()).eq(ExamTask::getId, taskReviewLog.getExamTaskId());
//                this.update(updateWrapper);
//            } else {
//                // 更新记录表状态
//                taskAfterPass(taskReviewLog.getReviewStatus(), examTaskId, examTaskPaperLog, sysUser);
//            }
//
//            if (taskReviewLog.getReviewStatus().name().equals(ReviewStatusEnum.PASS.name())) {
//                // 校验是否可以提交打印状态
//                ExamTask examTask = this.getById(examTaskId);
//                try {
//                    printCommonService.checkData(examTask.getSchoolId(), examTask.getCourseCode(), examTask.getPaperNumber(), sysUser);
//                } catch (IOException e) {

//                }
//            }
//        }
        return true;
    }

    private void taskAfterPass(ReviewStatusEnum statusEnum, Long examTaskId, ExamTaskPaperLog
            examTaskPaperLog, SysUser sysUser) {
        // 更新记录表状态
        examTaskPaperLog.setReview(true);
        examTaskPaperLog.setReviewStatus(statusEnum);
        examTaskPaperLog.setUpdateId(sysUser.getId());
        examTaskPaperLog.setUpdateTime(System.currentTimeMillis());
        examTaskPaperLogService.updateById(examTaskPaperLog);

        if (statusEnum.name().equals(ReviewStatusEnum.PASS.name())) {
            ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(examTaskId);
            String exposedPaperType = examTaskDetail.getExposedPaperType();
            Set<String> exposedPaperSet = StringUtils.isBlank(exposedPaperType) ? new HashSet<>() : new HashSet<>(Arrays.asList(exposedPaperType.split(",")));
            String unexposedPaperType = examTaskPaperLog.getPaperType();
            Set<String> unexposedPaperSet = StringUtils.isBlank(unexposedPaperType) ? new HashSet<>() : new HashSet<>(Arrays.asList(unexposedPaperType.split(",")));
            String newUnexposedPaperType;
            //未曝光类型
            if (!exposedPaperSet.isEmpty()) {
                for (String exposedPaper : exposedPaperSet) {
                    unexposedPaperSet.remove(exposedPaper);
                }
                newUnexposedPaperType = String.join(",", unexposedPaperSet);
            } else {
                newUnexposedPaperType = unexposedPaperType;
            }
            // 通过，更新正式表数据
            UpdateWrapper<ExamTaskDetail> examTaskDetailUpdateWrapper = new UpdateWrapper<>();
            examTaskDetailUpdateWrapper.lambda().set(ExamTaskDetail::getPaperType, examTaskPaperLog.getPaperType())
                    .set(ExamTaskDetail::getPaperAttachmentIds, examTaskPaperLog.getPaperAttachmentIds())
                    .set(ExamTaskDetail::getCardId, examTaskPaperLog.getCardId())
                    .set(ExamTaskDetail::getUnexposedPaperType, newUnexposedPaperType)
                    .eq(ExamTaskDetail::getExamTaskId, examTaskId);
            examTaskDetailService.update(examTaskDetailUpdateWrapper);
        }
    }

    @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()));
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        Page<ExamTaskDetailDto> page = new Page<>(pageNumber, pageSize);
        return this.baseMapper.listTaskPaper(page, schoolId, semesterId, examId, courseCode, paperNumber, startTime, endTime, orgIds, containsQuestionTeacher, sysUser.getId(), makeMethod, cardRuleId);
    }

    @Override
    public List<RelatePaperDto> listPaperTypes(Long examTaskId, Long printPlanId, String courseCode) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        List<RelatePaperDto> list = this.baseMapper.listPaperNumbers(schoolId, courseCode, ExamStatusEnum.SUBMIT.name());
        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;

        /*QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskId).eq(ExamTaskDetail::getEnable, true);
        ExamTaskDetail examTaskDetail = this.examTaskDetailService.getOne(queryWrapper);
        List<RelatePaperDto> list = new ArrayList<>();
        if (examTaskDetail != null) {
            // 抽取规则
            ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);
            DrawRuleEnum drawRule = examPrintPlan.getDrawRule();

            RelatePaperDto relatePaperDto = new RelatePaperDto();
            // 抽取一次
            String paperTypes;
            if (DrawRuleEnum.ONE.equals(drawRule)) {
                // 未曝光类型
                paperTypes = examTaskDetail.getUnexposedPaperType();
            } else {
                // 所有类型
                paperTypes = examTaskDetail.getPaperType();
            }
            relatePaperDto.setPaperTypes(StringUtils.isBlank(paperTypes) ? null : Arrays.asList(paperTypes.split(",")));
            list.add(relatePaperDto);
        }
        return list;*/
    }

    @Transactional
    @Override
    public boolean 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("命题任务提交类型不能为空");
        }
        // 提交时，校验
        if (ExamStatusEnum.SUBMIT.name().equals(examTaskDetail.getOperateType())) {
            validSubmitParam(examTaskDetail);
        }

        // 更新examTask状态status
        ExamTask examTask = this.getById(examTaskDetail.getExamTaskId());

        if (ExamStatusEnum.SUBMIT.name().equals(examTaskDetail.getOperateType())) {
            //TODO 加入从前端获取userId
            // 审核一级
            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.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
                map.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
                map.computeIfAbsent(SystemConstant.APPROVE_USER_IDS, v -> examTaskDetail.getApproveUserIds());
                activitiService.taskApprove(map);
            }
        }

        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);

        QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskDetail.getExamTaskId());
        ExamTaskDetail detail = examTaskDetailService.getOne(queryWrapper);
        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());
        return examTaskDetailService.saveOrUpdate(examTaskDetail);
    }

    @Override
    @Transactional
    public boolean status(ExamTask examTask) {
        Optional.ofNullable(examTask.getId()).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("参数有误"));
        ExamTask task = this.getById(examTask.getId());
        if (!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);
        return true;
    }

    @Override
    public ExamTaskDetailCardDto applyGetOne(Long examTaskId, String source) {
        return this.baseMapper.applyGetOne(examTaskId, source);
    }

    /**
     * 查询我的工作台
     *
     * @param iPage
     * @param userId
     * @param schoolId
     * @param status
     * @return
     */
    @Override
    public IPage<WorkResult> queryByMyWork(IPage<Map> iPage, Long userId, Long schoolId, String... status) {
        return examTaskMapper.queryByMyWork(iPage, userId, schoolId, status);
    }

    @Override
    public IPage<WorkResult> queryUserByMyWork(IPage<Map> iPage, Long userId, Long schoolId, String... status) {
        return examTaskMapper.queryUserByMyWork(iPage, userId, schoolId, status);
    }

    /**
     * 查询我的工作台-审核待办
     *
     * @param iPage
     * @param userId
     * @param schoolId
     * @param status
     * @return
     */
    @Override
    public IPage<WorkResult> queryByMyWorkSubmit(IPage<Map> iPage, Long userId, Long schoolId, ExamStatusEnum
            status) {
        return examTaskMapper.queryByMyWorkSubmit(iPage, userId, schoolId, Objects.nonNull(status) ? status.name() : null);
    }

    @Override
    public void paperDownload(HttpServletResponse response, Long examTaskId) throws Exception {
        String schoolId = ServletUtil.getRequestHeaderSchoolId().toString();
        ExamTask examTask = this.getById(examTaskId);
        QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskId);
        ExamTaskDetail examTaskDetail = examTaskDetailService.getOne(queryWrapper);
        if (examTaskDetail == null) {
            throw ExceptionResultEnum.ERROR.exception("数据异常");
        }
        String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
        List<JSONObject> jsonObjectList = JSONObject.parseArray(paperAttachmentIds, JSONObject.class);
        // 本地保存目录
        String rootPath = SystemConstant.TEMP_FILES_DIR + File.separator + System.currentTimeMillis();
        List<File> fileList = new ArrayList<>();
        // 试卷
        for (JSONObject jsonObject : jsonObjectList) {
            String id = jsonObject.get("attachmentId").toString();
            String name = jsonObject.get("name").toString();
            if (StringUtils.isNotBlank(id)) {
                BasicAttachment attachment = basicAttachmentService.getById(id);
                if (attachment == null) {
                    throw ExceptionResultEnum.ERROR.exception("附件数据异常");
                }
                String paperPath = rootPath + File.separator + examTask.getPaperNumber();
                String fileName = "试卷" + "_" + examTask.getPaperNumber() + "_" + name + attachment.getType();
                File file = teachcloudCommonService.copyFile(paperPath, fileName, attachment);
                fileList.add(file);
            }
        }

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

            String cardPath = rootPath + File.separator + examTask.getPaperNumber();
            String cardHtmlPath = cardPath + File.separator + "题卡" + "_" + examTask.getPaperNumber() + SystemConstant.HTML_PREFIX;
            String cardPdfPath = cardPath + File.separator + "题卡" + "_" + examTask.getPaperNumber() + SystemConstant.PDF_PREFIX;

            ExamCardDetail examCardDetail = examCardDetailService.getByCardId(examCard.getId());
            String htmlContent;
            // 通用模板
            if (MakeMethodEnum.SELECT.equals(examCard.getMakeMethod())) {
                htmlContent = createPdfUtil.resetHtmlTemplateBar(examCardDetail.getHtmlContent());
            } else {
                BasicCardRule basicCardRule = basicCardRuleService.getById(examTask.getCardRuleId());
                htmlContent = createPdfUtil.replaceHtmlCard(examCardDetail, basicCardRule);
            }

            // html
            File htmlFile = new File(cardHtmlPath);
            if (!htmlFile.getParentFile().exists()) {
                htmlFile.getParentFile().mkdirs();
            }
            // 生成html文件
            FileCopyUtils.copy(htmlContent.getBytes(), htmlFile);
            fileList.add(htmlFile);
            // 转pdf文件
            File pdfFile = new File(cardPdfPath);
            if (!pdfFile.exists()) {
                pdfFile.createNewFile();
            }
            HtmlToPdfUtil.convert(cardHtmlPath, cardPdfPath, PageSizeEnum.A3);
            fileList.add(pdfFile);
        }
        if (fileList.size() == 0) {
            throw ExceptionResultEnum.ERROR.exception("没有可导出文件");
        }

        teachcloudCommonService.downloadFileAndZip(response, rootPath, fileList);
    }

    @Override
    public ExamTask getByCourseCodeAndPaperNumber(Long schoolId, String courseCode, String paperNumber) {
        QueryWrapper<ExamTask> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId)
                .eq(ExamTask::getCourseCode, courseCode)
                .eq(ExamTask::getPaperNumber, paperNumber);
        return this.getOne(queryWrapper);
    }

    @Override
    public IPage<ClientExamTaskDto> listTryTask(Long schoolId, String machineCode, Long orgId, Long
            printPlanId, String courseCode, String paperNumber, Boolean isTry, Boolean isPass, Integer pageNumber, Integer
                                                        pageSize) {
        Page<ClientExamTaskDto> page = new Page<>(pageNumber, pageSize);
        // 印刷任务状态为印刷中（PRINTING）,考场状态为待打印（WAITING）
        String[] examDetailStatus = {ExamDetailStatusEnum.NEW.name(), ExamDetailStatusEnum.READY.name()};
        return this.baseMapper.listClientExamTaskPage(page, schoolId, machineCode, printPlanId, courseCode, paperNumber, isTry, isPass, orgId, PrintPlanStatusEnum.PRINTING.name(), examDetailStatus);
    }

    @Override
    public List<ClientExamTaskDto> listTryTask(Long schoolId, String machineCode, Long orgId, Long
            printPlanId, String courseCode, String paperNumber, Boolean isTry, Boolean isPass) {
        // 印刷任务状态为印刷中（PRINTING）,考场状态为待打印（WAITING）
        String[] examDetailstatus = {ExamDetailStatusEnum.NEW.name(), ExamDetailStatusEnum.READY.name()};
        List<ClientExamTaskDto> examTaskDtoIPage = this.baseMapper.listClientExamTaskPage(schoolId, machineCode, printPlanId, courseCode, paperNumber, isTry, isPass, orgId, PrintPlanStatusEnum.PRINTING.name(), examDetailstatus);
        return examTaskDtoIPage;
    }

    @Transactional
    @Override
    public void verifyCourseCodeByPaperNumber(Long schoolId, String paperNumber, String courseCode) {
        List<ExamTask> examTaskList = this.list(new QueryWrapper<ExamTask>().lambda()
                .eq(ExamTask::getPaperNumber, paperNumber).eq(ExamTask::getSchoolId, schoolId));
        if (examTaskList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception("异常 ：[试卷编号]" + paperNumber + "有多条命题任务记录");
        } else if (examTaskList.size() == 1) {
            ExamTask examTask = examTaskList.get(0);
            if (!courseCode.equals(examTask.getCourseCode())) {
                throw ExceptionResultEnum.ERROR.exception("要导入的考务数据的试卷编号在命题任务中已存在,但该试卷编号对应的考务数据课程代码和命题任务中的课程代码不匹配  \n【试卷编号】 ： " + paperNumber
                        + "\n【考务导入的课程编码】 : " + courseCode
                        + "\n【命题任务的课程编码】 : " + examTask.getCourseCode());
            }
        }
    }

    @Override
    public void checkDataByExamination(Long examDetailId, SysUser user) throws IOException {
        boolean judge = true;
        ExamDetail examDetail = examDetailService.getById(examDetailId);
        if (Objects.isNull(examDetail)) {
            throw ExceptionResultEnum.ERROR.exception("考务-考场不存在");
        }
        List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.list(new QueryWrapper<ExamDetailCourse>().lambda()
                .eq(ExamDetailCourse::getExamDetailId, examDetailId)
                .eq(ExamDetailCourse::getSchoolId, examDetail.getSchoolId()));
        if (examDetailCourseList.size() < 1) {
            throw ExceptionResultEnum.ERROR.exception("考务-课程不存在");
        }
        for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
            String courseCode = examDetailCourse.getCourseCode();
            String paperNumber = examDetailCourse.getPaperNumber();
            Long schoolId = examDetailCourse.getSchoolId();
            ExamTask examTask = this.getOne(new QueryWrapper<ExamTask>().lambda()
                    .eq(ExamTask::getSchoolId, schoolId)
                    .eq(ExamTask::getCourseCode, courseCode)
                    .eq(ExamTask::getPaperNumber, paperNumber));
            if (examTask == null) {
                judge = false;
            } else if (!ExamStatusEnum.SUBMIT.equals(examTask.getStatus())) {
                judge = false;
            }
        }
        if (judge) {
            Map<String, Object> map = tbTaskService.saveTask(TaskTypeEnum.CREATE_PDF, examDetail.getPrintPlanId(), user, examDetail.getId());
            //            asyncCreatePdfTempleteService.createPdf(map, null);
            MqDto mqDto = new MqDto(MqTagEnum.PDF.getCode(), map, String.valueOf(map.get(SystemConstant.TB_TASK_ID)));
            redisUtil.sendMessage(mqDto.getTopic(), mqDto);
        }
    }

    @Override
    public List<ExamTask> listExamTaskByCardId(Long cardId) {
        return this.baseMapper.listExamTaskByCardId(cardId);
    }

    @Override
    public ReviewSampleDto findReviewSampleInfoByExamTaskId(Long examTaskId) {
        List<ReviewSampleDto> reviewSampleDtoList = examTaskMapper.listReviewSampleInfoByExamTaskId(examTaskId);
        if (reviewSampleDtoList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception("不能找到所选命题任务[" + examTaskId + "]的审核样本信息");
        }
        return reviewSampleDtoList.get(0);
    }

    /**
     * 统计截止到当天为止的即将逾期的审核待办任务总数
     */
    @Override
    public void sendSmsExpireTask() {
        SysConfig sysConfig = sysConfigService.getByKey("sys.warning.days");
        if (sysConfig != null && StringUtils.isNotBlank(sysConfig.getConfigValue())) {
            Integer configValue = Integer.valueOf(sysConfig.getConfigValue());
            // 当天15点
            Calendar c1 = Calendar.getInstance();
            c1.set(Calendar.HOUR_OF_DAY, 15);
            c1.set(Calendar.MINUTE, 0);
            c1.set(Calendar.SECOND, 0);
            long startTime = c1.getTimeInMillis();
            // 3天后
            Calendar c2 = Calendar.getInstance();
            c2.setTime(c1.getTime());
            c2.add(Calendar.DATE, configValue);
            long endTime = c2.getTimeInMillis();
            // 命题任务到期预警
            String[] examTaskStatus1 = {ExamStatusEnum.SUBMIT.name()};
            List<ExamTask> examTasks1 = this.baseMapper.listExamTaskExpire(startTime, endTime, examTaskStatus1);
            if (examTasks1 != null && examTasks1.size() > 0) {
                Map<Long, List<String>> listMap = examTasks1.stream().collect(Collectors.groupingBy(ExamTask::getUserId, Collectors.mapping(m -> m.getId().toString(), Collectors.toList())));
                for (Map.Entry<Long, List<String>> entry : listMap.entrySet()) {
                    basicMessageService.sendNoticeExpireOrOverdue(MessageEnum.NOTICE_OF_EXAM_TASK_WILL_EXPIRE, entry.getKey(), entry.getValue());
                }
            }
            // 命题分配到期预警
            String[] examTaskStatus2 = {ExamStatusEnum.SUBMIT.name()};
            List<ExamTask> examTasks2 = this.baseMapper.listExamTaskExpire(startTime, endTime, examTaskStatus2);
            if (examTasks2 != null && examTasks2.size() > 0) {
                Map<Long, List<String>> listMap = examTasks2.stream().collect(Collectors.groupingBy(ExamTask::getCreateId, Collectors.mapping(m -> m.getId().toString(), Collectors.toList())));
                for (Map.Entry<Long, List<String>> entry : listMap.entrySet()) {
                    basicMessageService.sendNoticeExpireOrOverdue(MessageEnum.NOTICE_OF_ALLOCATION_WILL_EXPIRE, entry.getKey(), entry.getValue());
                }
            }

            // 审核待办到期预警
            List<ExamTask> examTasks3 = this.baseMapper.listExamTaskAuditExpire(startTime, endTime);
            if (examTasks3 != null && examTasks3.size() > 0) {
                Map<Long, List<String>> listMap = examTasks3.stream().collect(Collectors.groupingBy(ExamTask::getCreateId, Collectors.mapping(m -> m.getId().toString(), Collectors.toList())));
                for (Map.Entry<Long, List<String>> entry : listMap.entrySet()) {
                    basicMessageService.sendNoticeExpireOrOverdue(MessageEnum.NOTICE_OF_AUDIT_WILL_EXPIRE, entry.getKey(), entry.getValue());
                }
            }
        }
    }

    /**
     * 统计当天逾期的审核任务增量
     */
    @Override
    public void sendSmsOverdueTask() {
        // 当天9点
        Calendar c1 = Calendar.getInstance();
        c1.set(Calendar.HOUR_OF_DAY, 9);
        c1.set(Calendar.MINUTE, 0);
        c1.set(Calendar.SECOND, 0);
        long endTime = c1.getTimeInMillis();

        // 1天前
        Calendar cal = Calendar.getInstance();
        cal.setTime(c1.getTime());
        cal.add(Calendar.DATE, -1);
        long startTime = cal.getTimeInMillis();
        // 命题任务已逾期
        String[] examTaskStatus1 = {ExamStatusEnum.SUBMIT.name()};
        List<ExamTask> examTasks1 = this.baseMapper.listExamTaskExpire(startTime, endTime, examTaskStatus1);
        if (examTasks1 != null && examTasks1.size() > 0) {
            Map<Long, List<String>> listMap = examTasks1.stream().collect(Collectors.groupingBy(ExamTask::getUserId, Collectors.mapping(m -> m.getId().toString(), Collectors.toList())));
            for (Map.Entry<Long, List<String>> entry : listMap.entrySet()) {
                basicMessageService.sendNoticeExpireOrOverdue(MessageEnum.NOTICE_OF_EXAM_TASK_OVERDUE, entry.getKey(), entry.getValue());
            }
        }
        // 命题分配已逾期
        String[] examTaskStatus2 = {ExamStatusEnum.SUBMIT.name()};
        List<ExamTask> examTasks2 = this.baseMapper.listExamTaskExpire(startTime, endTime, examTaskStatus2);
        if (examTasks2 != null && examTasks2.size() > 0) {
            Map<Long, List<String>> listMap = examTasks2.stream().collect(Collectors.groupingBy(ExamTask::getCreateId, Collectors.mapping(m -> m.getId().toString(), Collectors.toList())));
            for (Map.Entry<Long, List<String>> entry : listMap.entrySet()) {
                basicMessageService.sendNoticeExpireOrOverdue(MessageEnum.NOTICE_OF_ALLOCATION_OVERDUE, entry.getKey(), entry.getValue());
            }
        }

        // 审核待办已逾期
        List<ExamTask> examTasks3 = this.baseMapper.listExamTaskAuditExpire(startTime, endTime);
        if (examTasks3 != null && examTasks3.size() > 0) {
            Map<Long, List<String>> listMap = examTasks3.stream().collect(Collectors.groupingBy(ExamTask::getCreateId, Collectors.mapping(m -> m.getId().toString(), Collectors.toList())));
            for (Map.Entry<Long, List<String>> entry : listMap.entrySet()) {
                basicMessageService.sendNoticeExpireOrOverdue(MessageEnum.NOTICE_OF_AUDIT_OVERDUE, entry.getKey(), entry.getValue());
            }
        }
    }

    @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;
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        Set<Long> orgIds = teachcloudCommonService.listSubOrgIds(null);
        return this.baseMapper.listTaskPaper(schoolId, semesterId, examId, courseCode, paperNumber, cardRuleId, makeMethod, startTime, endTime, 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 examTaskMapper.getFlowTaskReadyList(iPage, schoolId, orgId, String.valueOf(userId));
    }

    @Transactional
    @Override
    public void switchCard(Long examTaskId) {
        ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(examTaskId);
        if (examTaskDetail == null) {
            throw ExceptionResultEnum.ERROR.exception("命题任务不存在");
        }
        if (StringUtils.isNotBlank(examTaskDetail.getExposedPaperType())) {
            throw ExceptionResultEnum.ERROR.exception("已有试卷曝光，不能切换题卡");
        }
        if (Objects.nonNull(examTaskDetail.getCardId())) {
            ExamCard examCard = examCardService.getById(examTaskDetail.getCardId());
            if (examCard == null) {
                throw ExceptionResultEnum.ERROR.exception("题卡不存在");
            }
            if (MakeMethodEnum.CUST.equals(examCard.getMakeMethod())) {
                throw ExceptionResultEnum.ERROR.exception("客服制卡方式，不能切换");
            }

            // 删除自建题卡
            if (!MakeMethodEnum.SELECT.name().equals(examTaskDetail.getMakeMethod())) {
                UpdateWrapper<ExamCardDetail> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda().eq(ExamCardDetail::getCardId, examTaskDetail.getCardId());
                examCardDetailService.remove(updateWrapper);

                examCardService.removeById(examTaskDetail.getCardId());
            }

            UpdateWrapper<ExamTaskDetail> examTaskDetailUpdateWrapper = new UpdateWrapper<>();
            examTaskDetailUpdateWrapper.lambda().set(ExamTaskDetail::getCardId, null)
                    .set(ExamTaskDetail::getMakeMethod, null)
                    .eq(ExamTaskDetail::getId, examTaskDetail.getId());
            examTaskDetailService.update(examTaskDetailUpdateWrapper);
        }
    }

    /**
     * 根据流程状态查找命题任务
     *
     * @param schoolId
     * @param courseCode
     * @param courseName
     * @param paperNumber
     * @param flowStatus
     * @return
     */
    @Override
    public List<ExamTask> findByFlowStatus(Long schoolId, String courseCode, String courseName, String paperNumber, FlowStatusEnum flowStatus) {
        return examTaskMapper.findByFlowStatus(schoolId, courseCode, courseName, paperNumber, Objects.nonNull(flowStatus) ? flowStatus.name() : null);
    }

    @Transactional
    @Override
    public void removeByExamTaskId(Long examTaskId) {
        // 删除任务
        this.removeById(examTaskId);
        ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamTaskId(examTaskId);
        if (examTaskDetail != null) {
            if (examTaskDetail.getCardId() != null && !MakeMethodEnum.SELECT.name().equals(examTaskDetail.getMakeMethod())) {
                examCardService.removeById(examTaskDetail.getCardId());
                UpdateWrapper<ExamCardDetail> examCardDetailUpdateWrapper = new UpdateWrapper<>();
                examCardDetailUpdateWrapper.lambda().eq(ExamCardDetail::getCardId, examTaskDetail.getCardId());
                examCardDetailService.remove(examCardDetailUpdateWrapper);
            }
        }
        examTaskDetailService.removeById(examTaskDetail.getId());
    }

    @Transactional
    @Override
    public ExamTask saveExamTaskAndExamTaskDetail(ExamTaskApplyParam examTaskApplyParam) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        ExamTask examTask = examTaskApplyParam.getExamTask();
        String courseCode = examTask.getCourseCode();
        Long examId = examTask.getExamId();
        ExamTaskDetail examTaskDetail = examTaskApplyParam.getExamTaskDetail();
        try {
            BasicExamRule basicExamRule = basicExamRuleService.getBySchoolId();
            if (basicExamRule == null) {
                throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
            }

            if (StringUtils.isNotBlank(examTask.getPaperNumber())) {
                QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).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("试卷编号已存在");
                }
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                String paperNumber = printCommonService.createPaperNumber(schoolId);
                examTask.setPaperNumber(paperNumber);
            }
            if (Objects.nonNull(examTask.getId())) {
                ExamTask task = this.getById(examTask.getId());
                examTask.setFlowId(task.getFlowId());
            }

            examTask.setId(Objects.isNull(examTask.getId()) ? SystemConstant.getDbUuid() : examTask.getId());
            examTask.setCardRuleId(basicPrintConfigService.getByExamIdAndCourseCode(examId, courseCode).getCardRuleId());
            examTask.setPaperNumberId(SystemConstant.getDbUuid());
            examTask.setSchoolId(schoolId);
            examTask.setOrgId(basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, examTask.getCourseCode()));
            examTask.setCreateId(sysUser.getId());
            examTask.setReview(basicExamRule.getReview());
            examTask.setUserId(sysUser.getId());
            examTask.updateInfo(sysUser.getId());

            examTask.setStatus(ExamStatusEnum.valueOf(examTaskDetail.getOperateType()));
            if (Objects.isNull(examTask.getFlowId())) {
                if (basicExamRule.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);
                }
            }
            this.saveOrUpdate(examTask);

            // 提交时，校验
            if (ExamStatusEnum.SUBMIT.name().equals(examTaskDetail.getOperateType())) {
                //TODO 加入从前端获取userId
                validSubmitParam(examTaskDetail);
                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.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
                        map.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
                        map.computeIfAbsent(SystemConstant.APPROVE_USER_IDS, v -> examTaskApplyParam.getApproveUserIds());
                        activitiService.taskApprove(map);
                    }
                }
            }

            QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTask.getId());
            ExamTaskDetail detail = examTaskDetailService.getOne(queryWrapper);
            if (detail != null) {
                examTaskDetail.setId(detail.getId());
                // 已曝光试卷和未曝光试卷赋值
                examTaskDetail.setExposedPaperType(detail.getExposedPaperType());
                examTaskDetail.setUnexposedPaperType(examTaskDetail.getPaperType());
            } else {
                // 已曝光试卷和未曝光试卷赋值（新增时，已曝光试卷为null，未曝光试卷为paper_type）
                examTaskDetail.setId(SystemConstant.getDbUuid());
                examTaskDetail.setUnexposedPaperType(examTaskDetail.getPaperType());
            }
            examTaskDetail.setExamTaskId(examTask.getId());
            examTaskDetail.setUpdateId(sysUser.getId());
            examTaskDetail.setUpdateTime(System.currentTimeMillis());

            examTaskDetailService.saveOrUpdate(examTaskDetail);

        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof ActivitiObjectNotFoundException) {
                ResultUtil.error("流程不存在");
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return examTask;
    }

    @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;
    }

    /**
     * 获取自己可以审核的流程id集合
     *
     * @param currentUserId
     * @return
     */
    @Override
    public List<Long> findFlowIdToMySelf(Long currentUserId) {
        return examTaskMapper.findFlowIdToMySelf(currentUserId);
    }

    /**
     * 获取不是自己可以审核的流程id集合
     */
    @Override
    public List<Long> findFlowIdToNotMySelf(Long currentUserId) {
        return examTaskMapper.findFlowIdToNotMySelf(currentUserId);
    }

    @Transactional
    @Override
    public ExamTask submitTaskApply(ExamTaskApplyTemp examTaskApplyTemp) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        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("自定义流程数据已发生改变，请刷新重试");
            }
        }

        JSONObject jsonObject = JSONObject.parseObject(examTaskApplyTemp.getExamTaskContent());

        // 新建命题任务
        ExamTask examTask = JSONObject.parseObject(String.valueOf(jsonObject.get("examTask")), ExamTask.class);
        try {
            BasicExamRule basicExamRule = basicExamRuleService.getBySchoolId();
            if (basicExamRule == null) {
                throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
            }

            if (StringUtils.isNotBlank(examTask.getPaperNumber())) {
                QueryWrapper<ExamTask> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.lambda().eq(ExamTask::getSchoolId, schoolId).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("试卷编号已存在");
                }
            } else {
                // 试卷编号生成规则：年月日（例如：20100419）+0000（例如：0001）顺序编号
                String paperNumber = printCommonService.createPaperNumber(schoolId);
                examTask.setPaperNumber(paperNumber);
            }

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

            examTask.setId(SystemConstant.getDbUuid());
            examTask.setPaperNumberId(SystemConstant.getDbUuid());
            examTask.setSchoolId(schoolId);
            examTask.setOrgId(basicCourseService.getOrgIdBySchoolIdAndCourseCode(schoolId, examTask.getCourseCode()));
            examTask.setReview(basicExamRule.getReview());
            examTask.setUserId(sysUser.getId());
            examTask.setStatus(ExamStatusEnum.SUBMIT);
            examTask.setCreateId(sysUser.getId());
            examTask.setCreateTime(System.currentTimeMillis());

            BasicPrintConfig basicPrintConfig = basicPrintConfigService.getByExamIdAndCourseCode(examTask.getExamId(), examTask.getCourseCode());
            if (basicPrintConfig == null) {
                throw ExceptionResultEnum.ERROR.exception("印品配置未设置");
            }
            examTask.setCardRuleId(basicPrintConfig.getCardRuleId());

            if (Objects.isNull(examTask.getFlowId())) {
                if (basicExamRule.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);
                }
            }
            this.save(examTask);

            // 新建试卷
            ExamTaskDetail examTaskDetail = JSONObject.parseObject(String.valueOf(jsonObject.get("examTaskDetail")), ExamTaskDetail.class);
            validSubmitParam(examTaskDetail);
            // 已曝光试卷和未曝光试卷赋值（新增时，已曝光试卷为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);

            BasicExam basicExam = basicExamService.getById(examTask.getExamId());
            if (basicExam == null || basicExam.getExamModel() == null) {
                throw ExceptionResultEnum.ERROR.exception("未查到考试信息");
            }

            if (ExamModelEnum.MODEL1.equals(basicExam.getExamModel()) || ExamModelEnum.MODEL2.equals(basicExam.getExamModel())) {
                // 创建印刷计划
                ExamDetailParams examDetailParams = JSONObject.parseObject(String.valueOf(jsonObject.get("examDetail")), ExamDetailParams.class);
                ConvertUtil.analyzeAndCompareDateAndTime(examDetailParams.getExamStartTime(), examDetailParams.getExamEndTime());
                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());
                examPrintPlan.setExamStartTime(examDetailParams.getExamStartTime());
                examPrintPlan.setExamEndTime(examDetailParams.getExamEndTime());
                examPrintPlan.setPrintContent(String.join(",", JSONObject.parseArray(basicPrintConfig.getPrintContent(), String.class)));
                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);
                examPrintPlan.setSyncStatus(ExamPrintPlanSyncStatusEnum.INIT);
                examPrintPlanService.save(examPrintPlan);

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


                        SerialNumberParams serialNumberParams = new SerialNumberParams("packageCode-" + schoolId, "1", 6);
                        ExamDetail examDetail = new ExamDetail();
                        examDetail.setId(SystemConstant.getDbUuid());
                        examDetail.setPackageCode(printCommonService.createTempNumber(serialNumberParams));
                        examDetail.setSchoolId(schoolId);
                        examDetail.setOrgId(sysUser.getOrgId());
                        examDetail.setPrintPlanId(examPrintPlan.getId());
                        examDetail.setPrintPlanName(examPrintPlan.getName());
                        examDetail.setExamPlace(StringUtils.isBlank(examDetailList.getExamPlace()) ? "考点" + i : examDetailList.getExamPlace()); // 默认填充班级所在校区
                        examDetail.setExamRoom(StringUtils.isBlank(examDetailList.getExamRoom()) ? clazzName : examDetailList.getExamRoom()); // 默认填充班级
                        examDetail.setStatus(ExamDetailStatusEnum.NEW);
                        examDetail.setExamStartTime(examDetailParams.getExamStartTime());
                        examDetail.setExamEndTime(examDetailParams.getExamEndTime());


                        examDetail.setTotalSubjects(count);
                        examDetail.setExamDataSource(ExamDataSourceEnum.EXAM_TASK);
                        examDetail.setNormal(true);
                        examDetail.setPrintHouseId(examDetailList.getPrintHouseId());
                        examDetail.setCreateId(sysUser.getId());
                        examDetail.setCreateTime(System.currentTimeMillis());
                        examDetailService.save(examDetail);

                        List<String> classIds = Arrays.asList(examDetailList.getClassId().split(","));
                        ExamDetailCourse examDetailCourse = new ExamDetailCourse();
                        examDetailCourse.setId(SystemConstant.getDbUuid());
                        examDetailCourse.setSchoolId(schoolId);
                        examDetailCourse.setOrgId(sysUser.getOrgId());
                        examDetailCourse.setExamDetailId(examDetail.getId());
                        examDetailCourse.setCourseCode(examTask.getCourseCode());
                        examDetailCourse.setCourseName(examTask.getCourseName());
                        examDetailCourse.setPaperNumber(examTask.getPaperNumber());
                        examDetailCourse.setClazzId(examDetailList.getClassId());
                        examDetailCourse.setTotalSubjects(examDetailList.getStudentCount());
                        examDetailCourse.setCreateId(sysUser.getId());
                        examDetailCourseService.save(examDetailCourse);

                        // 更新实际考生数量
                        List<ExamStudent> examStudents = printCommonService.createBatchStudent(schoolId, examDetailList.getExtendFields(), classIds, examDetailCourse.getId(), sysUser);
                        if (!CollectionUtils.isEmpty(examStudents)) {
                            examStudentService.insertBatch(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);
                        }
                        i++;
                    }
                } else if (ExamModelEnum.MODEL2.equals(basicExam.getExamModel())) {
                    SerialNumberParams serialNumberParams = new SerialNumberParams("packageCode-" + schoolId, "1", 6);
                    ExamDetail examDetail = new ExamDetail();
                    examDetail.setId(SystemConstant.getDbUuid());
                    examDetail.setPackageCode(printCommonService.createTempNumber(serialNumberParams));
                    examDetail.setSchoolId(schoolId);
                    examDetail.setOrgId(sysUser.getOrgId());
                    examDetail.setPrintPlanId(examPrintPlan.getId());
                    examDetail.setPrintPlanName(examPrintPlan.getName());
                    examDetail.setExamPlace("考点1"); // 默认填充班级所在校区
                    examDetail.setExamRoom("考场1"); // 默认填充班级
                    examDetail.setStatus(ExamDetailStatusEnum.NEW);
                    examDetail.setExamStartTime(examDetailParams.getExamStartTime());
                    examDetail.setExamEndTime(examDetailParams.getExamEndTime());
                    examDetail.setTotalSubjects(examDetailParams.getPrintCount());
                    examDetail.setExamDataSource(ExamDataSourceEnum.EXAM_TASK);
                    examDetail.setNormal(true);
                    examDetail.setPrintCount(examDetailParams.getPrintCount());
                    examDetail.setPrintHouseId(examDetailParams.getPrintHouseId());
                    examDetail.setCreateId(sysUser.getId());
                    examDetail.setCreateTime(System.currentTimeMillis());
                    examDetailService.save(examDetail);

                    ExamDetailCourse examDetailCourse = new ExamDetailCourse();
                    examDetailCourse.setId(SystemConstant.getDbUuid());
                    examDetailCourse.setSchoolId(schoolId);
                    examDetailCourse.setOrgId(sysUser.getOrgId());
                    examDetailCourse.setExamDetailId(examDetail.getId());
                    examDetailCourse.setCourseCode(examTask.getCourseCode());
                    examDetailCourse.setCourseName(examTask.getCourseName());
                    examDetailCourse.setPaperNumber(examTask.getPaperNumber());
                    examDetailCourse.setTotalSubjects(examDetailParams.getPrintCount());
                    examDetailCourse.setCreateId(sysUser.getId());
                    examDetailCourseService.save(examDetailCourse);
                }
            }

            if (Objects.nonNull(examTask.getFlowId())) {
                //TODO 加入从前端获取userId
                // 审核一级
                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);
                }
            }
            return examTask;
        } 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 schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        // 1.basicInfo 部分
        ExamTask examTask = this.getById(examTaskId);
        if (Objects.isNull(examTask)) {
            throw ExceptionResultEnum.ERROR.exception("命题任务不存在");
        }
        String paperNumber = examTask.getPaperNumber();
        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).eq(SysOrg::getType, OrgTypeEnum.TEACHING_ROOM));
        if (Objects.isNull(teachingRoom)) {
            throw ExceptionResultEnum.ERROR.exception("找不到教研室");
        }
        String teachingRoomName = teachingRoom.getName();

        // 查询教研室所在学院
        List<SysOrg> collegeList = sysOrgService.findParentsByOrgId(teachingRoom.getId())
                .stream()
                .filter(e -> OrgTypeEnum.COLLEGE.equals(e.getType()))
                .collect(Collectors.toList());
//        List<SysOrg> collegeList = sysOrgService.findByConnectByParentId(teachingRoom.getId(),true)
//                .stream()
//                .filter(e -> OrgTypeEnum.COLLEGE.equals(e.getType()))
//                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collegeList)) {
            throw ExceptionResultEnum.ERROR.exception("找不到学院");
        }
        SysOrg college = collegeList.get(0);


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

        // 3.ApprovalInfo部分
        Boolean review = examTask.getReview();
        final String remark = "通过";
        final String link = ",";
        List<ApprovalInfo> approvalInfoList = new ArrayList<>();
        if (review) {
            approvalInfoList = tfFlowLogService.findApprovalInfoByFlowId(flowId);
        }

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

        // ---- 组装Dto
        ExamTaskApprovalFormDto examTaskApprovalFormDto = new ExamTaskApprovalFormDto();
        BasicInfo basicInfo = new BasicInfo();
        basicInfo.setCollegeId(college.getId());
        basicInfo.setCollegeName(college.getName());
        basicInfo.setTeachingRoomId(teachingRoomId);
        basicInfo.setTeachingRoomName(teachingRoomName);
        basicInfo.setPaperName(paperName);
        basicInfo.setPaperNumber(paperNumber);
        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(String paperNumber) {
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        ExamTask examTask = examTaskService.getOne(new QueryWrapper<ExamTask>().lambda().eq(ExamTask::getPaperNumber, paperNumber).eq(ExamTask::getSchoolId, schoolId));
        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 = examTaskMapper.findByFlowId(flowId);
        Optional.ofNullable(examTask).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("命题计划为空"));
        return examTask;
    }

    /**
     * 获取ProcessDefinitionId
     *
     * @param key
     * @return
     */
    @Override
    public String getProcessDefinitionIdByMaxVersion(String key) {
        return examTaskMapper.getProcessDefinitionIdByMaxVersion(key);
    }

    @Transactional
    @Override
    public void 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);
    }

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

    /**
     * 简单校验提交参数
     *
     * @param examTaskDetail
     */
    void validSubmitParam(ExamTaskDetail examTaskDetail) {
        if (StringUtils.isBlank(examTaskDetail.getPaperType())) {
            throw ExceptionResultEnum.ERROR.exception("没有设置卷型");
        }
        if (examTaskDetail.getCardId() == null) {
            throw ExceptionResultEnum.ERROR.exception("没有绑定题卡");
        }

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

        // 校验题卡是否提交
        ExamCard examCard = examCardService.getById(examTaskDetail.getCardId());
        if (!ExamCardStatusEnum.SUBMIT.name().equals(examCard.getStatus().name())) {
            throw ExceptionResultEnum.ERROR.exception("绑定的题卡未提交");
        }

        // 是否强制包含试卷
        BasicExamRule basicExamRule = basicExamRuleService.getBySchoolId();
        List<Map> paperAttachmentIds = JSONObject.parseArray(examTaskDetail.getPaperAttachmentIds(), Map.class);
        // 未上传试卷的类型个数
        long count = paperAttachmentIds.stream().filter(m -> StringUtils.isBlank(m.get("attachmentId").toString())).count();
        if (basicExamRule.getIncludePaper()) {
            if (CollectionUtils.isEmpty(paperAttachmentIds)) {
                throw ExceptionResultEnum.ERROR.exception("没有设置卷型");
            }
            if (count > 0) {
                throw ExceptionResultEnum.ERROR.exception("有部分卷型未上传试卷文件");
            }
        } else {
            // count == 0为全部上传，paperAttachmentIds.size() == count 为全部未上传
            if (count > 0 && paperAttachmentIds.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<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<Integer> backupCountList = examPackageDetailCell.stream().map(ExamPackageDetail::getBackupCount).distinct().collect(Collectors.toList());
                if (backupCountList.size() != 1) {
                    throw ExceptionResultEnum.ERROR.exception("印刷计划试卷备份数量异常");
                }
                // 试卷该考场备份数量
                Integer backupCount = backupCountList.get(0);
                // 班级主键','隔开
                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));
                        }
                    }
                }
                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);
                examRoomInfoList.add(examRoomInfo);
            }
        }
        ExamObjectDto examObjectDto = new ExamObjectDto();
        examObjectDto.setExamEndTime(examEndTime);
        examObjectDto.setExamStartTime(examStartTime);
        examObjectDto.setExamRoomInfoList(examRoomInfoList);
        examObjectDto.setPrintTotalCount(printTotalCount);
        return examObjectDto;
    }
}
