package cn.com.qmth.stmms.ms.admin.service;

import cn.com.qmth.stmms.ms.admin.dto.StudentDTO;
import cn.com.qmth.stmms.ms.admin.dto.StudentRelateDTO;
import cn.com.qmth.stmms.ms.commons.config.ImageCompressionConfig;
import cn.com.qmth.stmms.ms.commons.config.ImageConfig;
import cn.com.qmth.stmms.ms.commons.config.ImageServerConfig;
import cn.com.qmth.stmms.ms.commons.config.SystemConfig;
import cn.com.qmth.stmms.ms.commons.constant.SystemConstant;
import cn.com.qmth.stmms.ms.commons.utils.MD5Util;
import cn.com.qmth.stmms.ms.commons.utils.RandomUtil;
import cn.com.qmth.stmms.ms.commons.utils.excel.ExcelError;
import cn.com.qmth.stmms.ms.commons.utils.excel.ExcelReader;
import cn.com.qmth.stmms.ms.commons.utils.excel.ExcelReaderHandle;
import cn.com.qmth.stmms.ms.commons.utils.image.ImageCompression;
import cn.com.qmth.stmms.ms.core.cache.ParamCache;
import cn.com.qmth.stmms.ms.core.domain.*;
import cn.com.qmth.stmms.ms.core.domain.enums.TrialEnum;
import cn.com.qmth.stmms.ms.core.repository.*;
import cn.com.qmth.stmms.ms.core.vo.Subject;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.mockito.internal.exceptions.ExceptionIncludingMockitoWarnings;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据上传服务 Created by zhengmin on 2016/11/18.
 */
@Service
public class DataUploadService {
    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DataUploadService.class);

    @Autowired
    private StudentRepo studentRepo;

    @Autowired
    private ExamQuestionRepo examQuestionRepo;

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private SystemConfig systemConfig;

    @Autowired
    private ImageCompressionConfig compressionConfig;

    @Autowired
    private MarkTaskRepo markTaskRepo;

    @Autowired
    ImageConfig imageConfig;

    @Autowired
    MarkSubjectRepo markSubjectRepo;

    @Autowired
    WorkRepo workRepo;

    @Autowired
    RandomUtil randomUtil;

    @Autowired
    ImageServerConfig imageServerConfig;

    /**
     * 上传考生试卷数据
     *
     * @param studentId 考生id
     * @param subject   科目
     * @param in        图片文件输入流
     * @param isManual  是否手工绑定
     * @throws IOException
     */
    @Transactional
    public void uploadPaper(Long studentId, Subject subject, InputStream in, boolean isManual) throws Exception {
        long start = System.currentTimeMillis();
        LOGGER.info("准备生成裁切原图和缩略图:{}", start);
        File imageFileTemp = null;
        try {
            FileInputStream sheetIn = null;
            FileInputStream slicein = null;
            Student student = studentRepo.findOne(studentId);
            if (imageConfig.isCustomSubject()) {
                subject = Subject.CUSTOM;
            }
            if (ParamCache.paramMap.get(student.getWorkId()).getImageEncrypt() == 1) {
                //保存裁切原图+文件名加密
                String imageDir = systemConfig.getImageDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode();
                File out = new File(imageDir);
                if (!out.exists()) {
                    out.mkdirs();
                }

                String imageMd5 = MD5Util.getImageRuleMd5(student.getWorkId(), subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
                File imageFile = new File(imageDir + File.separator + imageMd5 + ".jpg");
                imageFileTemp = new File(imageDir + File.separator + imageMd5 + "temp.jpg");
                LOGGER.info("image存放目录:{}", imageFile.getPath());
                OutputStream outputStream = new FileOutputStream(imageFile);
                OutputStream outputStreamTemp = new FileOutputStream(imageFileTemp);
                int index = 0;
                byte[] bytes = new byte[1024];
                byte[] bytesEnc = new byte[1024];
                while ((index = in.read(bytes)) != -1) {
                    //将字节数组的数据全部写入到输出流中
                    for (int i = 0; i < index; i++) {
                        //通过异或运算加密
                        bytesEnc[i] = (byte) (bytes[i] ^ SystemConstant.SECRET_KEY);
                    }
                    outputStream.write(bytesEnc, 0, index);
                    outputStreamTemp.write(bytes, 0, index);
                }
                in.close();
                outputStream.flush();
                outputStream.close();
                outputStreamTemp.flush();
                outputStreamTemp.close();

                //生成缩略图
                String thumbDir = systemConfig.getThumbDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode();
                File thumb = new File(thumbDir);
                if (!thumb.exists()) {
                    thumb.mkdirs();
                }

                BufferedImage bufferedImage = ImageCompression.compress(imageFileTemp, compressionConfig);
                File thumbFile = new File(thumbDir + File.separator + imageMd5 + ".jpg");
                LOGGER.info("thumb存放目录:{}", thumbFile.getPath());
                OutputStream outputStream1 = new FileOutputStream(thumbFile);
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                ImageIO.write(bufferedImage, "jpg", os);
                InputStream in1 = new ByteArrayInputStream(os.toByteArray());

                SystemConstant.writeStream(in1, outputStream1);
                long end = System.currentTimeMillis();
                LOGGER.info("生成原图和缩略图耗时：{}", (end - start) / 1000 + "s");

                in1.close();
                outputStream1.flush();
                outputStream1.close();
                sheetIn = new FileInputStream(imageFile.getPath());
                slicein = new FileInputStream(thumbFile.getPath());
            } else {
                String savePath = systemConfig.getImageDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode();
                File out = new File(savePath);
                if (!out.exists()) {
                    out.mkdirs();
                }
                File outFile = new File(savePath + File.separator + student.getExamNumber() + ".jpg");
                if (!outFile.exists()) {
                    outFile.createNewFile();
                }
                OutputStream output = new FileOutputStream(outFile);
                byte[] buffer = new byte[in.available()];
                in.read(buffer);
                output.write(buffer);
                output.close();

                // 生成缩略图
                String thumbDir = systemConfig.getThumbDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode();
                File thumb = new File(thumbDir);
                if (!thumb.exists()) {
                    thumb.mkdirs();
                }
                BufferedImage bufferedImage = ImageCompression.compress(outFile, compressionConfig);
                String thumbFileName = thumbDir + File.separator + student.getExamNumber() + ".jpg";
                ImageIO.write(bufferedImage, "jpg", new File(thumbFileName));

                sheetIn = new FileInputStream(savePath + File.separator + student.getExamNumber() + ".jpg");
                slicein = new FileInputStream(thumbFileName);
            }
            Paper exist = paperRepo.findByWorkIdAndSubjectAndExamNumberAndTest(student.getWorkId(), subject,
                    student.getExamNumber(), TrialEnum.DEFAULT.getId());
            String sheetMD5 = DigestUtils.md5Hex(sheetIn);
            String sliceMD5 = DigestUtils.md5Hex(slicein);
            sheetIn.close();
            slicein.close();
            if (exist != null) {
                exist.setManual(isManual);
                exist.setUploadedOn(new Date());
                exist.setSheetMD5(sheetMD5);
                exist.setSliceMD5(sliceMD5);
                paperRepo.save(exist);
            } else {
                ExamQuestion examQuestion = examQuestionRepo.findByWorkIdAndSubjectAndAreaCodeAndTest(student.getWorkId(), subject,
                        student.getAreaCode(), TrialEnum.DEFAULT.getId());
                Long random = getRandom(student.getWorkId(), student.getExamNumber());
                Paper paper = new Paper(student.getWorkId(), null, subject, examQuestion, student, false, random);
                paper.setSheetMD5(sheetMD5);
                paper.setSliceMD5(sliceMD5);
                paper.setExamRoom(student.getExamRoom());
                paperRepo.save(paper);
                // 更新科目上传状态
                /**
                 * Long count =
                 * paperRepo.countByWorkIdAndExamNumber(student.getWorkId(),student.
                 * getExamNumber()); if(count == Subject.values().length){
                 * student.setUpload(true); studentRepo.save(student); }
                 */
                synchronized (this) {
                    String uploadStatus = student.getUploadStatus();
                    String replace = subject.toString() + ":1";
                    String regx = subject.toString() + ":[0-1]";
                    String newStatus = uploadStatus.replaceFirst(regx, replace);
                    student.setUploadStatus(newStatus);
                    student.setAbsent(false);
                    studentRepo.save(student);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //原图删除
            if (Objects.nonNull(imageFileTemp) && imageFileTemp.exists()) {
                imageFileTemp.delete();
            }
        }
    }

    @Transactional
    public List<ExcelError> uploadStudents(Long workId, final boolean isAbsent, InputStream inputStream) throws Exception {
        //有采集数据，不能导入
        List<Paper> papers = paperRepo.findByWorkId(workId);
        if (papers != null && papers.size() > 0) {
            throw new Exception("已有采集数据，不能导入考生数据");
        }
        examQuestionRepo.deleteByWorkId(workId);

        ExcelReader excelReader = new ExcelReader(StudentDTO.class);
        List<ExcelError> excelErrors = excelReader.reader(inputStream, new ExcelReaderHandle() {

            @Override
            public ExcelError handle(Object obj) {
                try {
                    StudentDTO dto = (StudentDTO) obj;
                    //校验excel文件中数据是否完整
                    if (!checkObjFieldIsNotNull(dto)) {
                        throw new RuntimeException("考生信息缺失");
                    }

                    String examNumber = dto.getExamNumber();
                    //如果没有设置区域代码，用准考证前2位作为区域代码
                    String areaCode = null;
                    if (dto.getAreaCode() == null || dto.getAreaCode().isEmpty()) {
                        areaCode = examNumber.substring(0, 2);
                        dto.setAreaCode(areaCode);
                    }

                    long isExist = examQuestionRepo.countByWorkIdAndAreaCode(workId, dto.getAreaCode());
                    if (isExist == 0) {
                        if (!imageConfig.isCustomSubject()) {
                            for (Subject subject : Subject.values()) {
                                ExamQuestion question = new ExamQuestion(dto.getAreaCode(), subject, workId, dto.getAreaName());
                                examQuestionRepo.save(question);
                            }
                        } else {
                            MarkSubject markSubject = markSubjectRepo.findOne(workId + "-" + Subject.CUSTOM.name());
                            ExamQuestion question = new ExamQuestion(dto.getAreaCode(), Subject.CUSTOM, markSubject.getName(), workId, dto.getAreaName());
                            examQuestionRepo.save(question);
                        }
                    }
                    Student student = studentRepo.findByWorkIdAndExamNumberAndTest(workId, dto.getExamNumber(), String.valueOf(TrialEnum.DEFAULT.getId()));
                    if (student == null && !isAbsent) {
                        student = new Student(dto.getName(), dto.getExamNumber(), dto.getAreaName(), dto.getAreaCode(),
                                workId, dto.getExamRoom(), dto.getSourceName(), String.valueOf(TrialEnum.DEFAULT.getId()), dto.getSchool());
                        if (imageConfig.isCustomSubject()) {
                            student.setUploadStatus("CUSTOM:0");
                        }
                        studentRepo.save(student);
                    } else if (student != null) {
                        if (isAbsent) {
                            student.setAbsent(isAbsent);
                            if (!imageConfig.isCustomSubject()) {
                                student.setUploadStatus("SX:0,SC:0,SM:0");
                            } else {
                                student.setUploadStatus("CUSTOM:0");
                            }

                            /**
                             * 查询该考生所有paper,若paper无任务则删除，有任务就保留
                             */
                            List<Paper> papers = paperRepo.findByWorkIdAndExamNumber(workId, student.getExamNumber());
                            if (!papers.isEmpty()) {
                                for (Paper paper : papers) {
                                    Long count = markTaskRepo.countByPaperId(paper.getId());
                                    if (count == 0) {
                                        paperRepo.delete(paper);
                                    }
                                }
                            }
                        } else {
                            student.setName(dto.getName());
                            student.setAreaCode(dto.getAreaCode());
                            student.setAreaName(dto.getAreaName());
                            student.setExamRoom(dto.getExamRoom());
                            student.setSourceName(dto.getSourceName());
                            student.setSchool(dto.getSchool());
                        }
                        studentRepo.save(student);
                    }
                    return null;
                } catch (RuntimeException e) {
                    //手动回滚
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    ExcelError excelError = new ExcelError();
                    excelError.setExcelErrorType(e.getMessage());
                    return excelError;
                }
            }
        });
        String errors = errorsString(excelErrors);
        if(errors.length() > 0) {
            throw new Exception(errors);
        }
        return excelErrors;
    }

    /**
     * 试评考生导入
     *
     * @param workId
     * @param subject
     * @param inputStream
     * @return
     * @throws Exception
     */
    @Transactional
    public List<ExcelError> uploadStudentsForTrial(Long workId, Subject subject, InputStream inputStream) throws Exception {
        Work work = workRepo.findOne(workId);
        if (Objects.isNull(work)) {
            throw new Exception("没有此工作区，请检查workId是否正确");
        }
        List<Paper> papers = paperRepo.findByWorkIdAndSubject(workId, subject);
        if(papers.isEmpty() || papers.size() == 0){
            throw new Exception("没有采集数据，不能导入试评数据");
        }
        List<MarkSubject> markSubjectList = work.getSubjects().stream().filter(o -> o.getId().toUpperCase().contains(subject.toString().toUpperCase()))
                .collect(Collectors.toList());
        MarkSubject markSubject = markSubjectList.get(0);
        if (markSubject.getTest() == TrialEnum.START_TRIAL.getId()) {
            throw new Exception("已经开始试评，请先停止试评再重新导入数据");
        }
        //更新科目
        markSubject.setTest(TrialEnum.INIT.getId());
        markSubjectRepo.save(markSubject);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(String.valueOf(TrialEnum.INIT.getId()), subject);
        //全量删除全量增加
        //1.试卷
        examQuestionRepo.deleteByWorkIdAndSubjectAndTest(workId, subject, TrialEnum.INIT.getId());
        //2.学生
        studentRepo.deleteByWorkIdAndTest(workId, jsonObject.toJSONString());
        //3.paper
        paperRepo.deleteByWorkIdAndSubjectAndTest(workId, subject, TrialEnum.INIT.getId());
        long startTime = System.currentTimeMillis();
        ExcelReader excelReader = new ExcelReader(StudentDTO.class);
        List<ExcelError> excelErrors = excelReader.reader(inputStream, new ExcelReaderHandle() {

            @Override
            public ExcelError handle(Object obj) throws Exception {
                try {
                    StudentDTO dto = (StudentDTO) obj;
                    //校验excel文件中数据是否完整
                    if (!checkObjFieldIsNotNull(dto)) {
                        throw new RuntimeException("考生信息缺失");
                    }
                    //复制学生给试评用
                    Student studentNew = null;
                    Student student = studentRepo.findByWorkIdAndExamNumberAndTest(workId, dto.getExamNumber(), String.valueOf(TrialEnum.DEFAULT.getId()));
                    if (student == null) {
                        throw new Exception("没有学生" + dto.getName() + "信息");
                    }
                    if (!student.getUploadStatus().contains(subject + ":1")) {
                        throw new Exception("学生" + student.getName() + "未上传完试卷");
                    }

                    //复制paper
                    Paper paper = paperRepo.findByWorkIdAndSubjectAndExamNumberAndAreaCodeAndTest(workId, subject, student.getExamNumber(), dto.getAreaCode(), TrialEnum.DEFAULT.getId());
                    if (Objects.isNull(paper)) {
                        paper = paperRepo.findByWorkIdAndSubjectAndExamNumberAndAreaCodeAndTest(workId, subject, student.getExamNumber(), dto.getAreaCode(), TrialEnum.START_FORMAL.getId());
                    }
                    if (Objects.isNull(paper) || paper.getIsMissing()) {
                        return null;
                    }

                    String examNumber = dto.getExamNumber();
                    //如果没有设置区域代码，用准考证前2位作为区域代码
                    String areaCode = null;
                    if (dto.getAreaCode() == null || dto.getAreaCode().isEmpty()) {
                        areaCode = examNumber.substring(0, 2);
                        dto.setAreaCode(areaCode);
                    }
                    //复制卷子给试评用
                    ExamQuestion questionNew = null;
                    ExamQuestion isExist = examQuestionRepo.findByWorkIdAndSubjectAndAreaCodeAndTest(workId, subject, dto.getAreaCode(), TrialEnum.DEFAULT.getId());
                    if (Objects.isNull(isExist)) {
                        throw new Exception("请先上传试卷");
                    } else {
                        ExamQuestion questionTemp = examQuestionRepo.findByWorkIdAndSubjectAndAreaCodeAndTest(workId, subject, dto.getAreaCode(), TrialEnum.INIT.getId());
                        questionNew = new ExamQuestion();
                        if (Objects.isNull(questionTemp)) {
                            BeanUtils.copyProperties(isExist, questionNew);
                            questionNew.setTest(TrialEnum.INIT.getId());
                            questionNew.setName(new StringBuffer("试评").append(subject.getName()).append(dto.getAreaCode()).toString());
                            questionNew.setId(null);
                            examQuestionRepo.save(questionNew);
                        } else {
                            BeanUtils.copyProperties(questionTemp, questionNew);
                        }
                    }

                    studentNew = new Student();
                    BeanUtils.copyProperties(student, studentNew);
                    studentNew.setId(null);
                    studentNew.setTest(jsonObject.toJSONString());
                    studentRepo.save(studentNew);

                    Paper paperNew = new Paper();
                    BeanUtils.copyProperties(paper, paperNew);
                    paperNew.setId(null);
                    paperNew.setTest(TrialEnum.INIT.getId());
                    Long random = getRandom(studentNew.getWorkId(), studentNew.getExamNumber());
                    paperNew.setRandomSeq(random);
                    paperNew.setQuestionName(questionNew.getName());
                    paperNew.setQuestionId(questionNew.getId());
                    paperNew.setLevel(null);
                    paperNew.setMarkByLeader(false);
                    paperNew.setSample(false);
                    paperNew.setRejected(false);
                    paperNew.setTagged(false);
                    paperNew.setSecretNumber(new StringBuffer(String.valueOf(subject.ordinal())).append(questionNew.getAreaCode()).append(random).toString());
                    paperNew.setActive(true);
                    paperNew.setBatchNo(startTime);
                    paperRepo.save(paperNew);
                } catch (Exception e) {
                    ExcelError excelError = new ExcelError();
                    excelError.setExcelErrorType(e.getMessage());
                    return excelError;
                }
                return null;
            }
        });
        if (Objects.nonNull(excelErrors) && excelErrors.size() > 0) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        String errors = errorsString(excelErrors);
        if(errors.length() > 0) {
            throw new Exception(errors);
        }
        return excelErrors;
    }

    /**
     * 清空考生数据 有上传数据无法清空
     *
     * @param workId
     */
    @Transactional
    public void clearStudents(Long workId) {
        Long isExist = paperRepo.countByWorkIdAndTest(workId, TrialEnum.DEFAULT.getId());
        if (isExist > 0) {
            throw new RuntimeException("有考生已上传，无法清空");
        }
        studentRepo.deleteByWorkId(workId);
        examQuestionRepo.deleteByWorkId(workId);
    }

    @Transactional
    public Paper savePaper(Student student, Subject subject, boolean isManual, String level) throws Exception {
        ExamQuestion examQuestion = examQuestionRepo.findByWorkIdAndSubjectAndAreaCodeAndTest(student.getWorkId(), subject, student.getAreaCode(), TrialEnum.DEFAULT.getId());
        Paper paper = paperRepo.findByWorkIdAndSubjectAndExamNumberAndTest(student.getWorkId(), subject, student.getExamNumber(), TrialEnum.DEFAULT.getId());
        if (paper == null) {
            Long random = getRandom(student.getWorkId(), student.getExamNumber());
            paper = new Paper(student.getWorkId(), null, subject, examQuestion, student, isManual, random);
        }
        paper.setUploadedOn(new Date());
        paper.setManual(isManual);

        if(Objects.nonNull(level) && level != ""){
            paper.setLevel(level);
            //设置为已发布任务的状态
            paper.setActive(true);
            //默认给1，与实际分档批次区分
            paper.setBatchNo(1l);
        }

        if(!imageServerConfig.isAliyunOss()) {
            String sheetPath = null;
            String slicePath = null;
            String fileName = student.getExamNumber();
            //0：随机码 1：考号
            if (ParamCache.paramMap.get(student.getWorkId()).getNameRule() == 1) {
//            fileName = DigestUtils.md5Hex(subject.name() + fileName);
                fileName = MD5Util.getImageRuleMd5(student.getWorkId(), subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
            }
            //图片是否加密
            Integer imageEnc = Optional.ofNullable(ParamCache.paramMap.get(student.getWorkId()).getImageEncrypt()).orElse(0);
            if (imageEnc == 1) {
                String imageMd5 = MD5Util.getImageRuleMd5(student.getWorkId(), subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
                sheetPath = systemConfig.getSheetDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode() + File.separator + imageMd5 + ".jpg";
                slicePath = systemConfig.getImageDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode() + File.separator + imageMd5 + ".jpg";
            } else {
                sheetPath = systemConfig.getSheetDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode() + File.separator + fileName + ".jpg";
                slicePath = systemConfig.getImageDir() + File.separator + student.getWorkId() + File.separator + subject
                        + File.separator + student.getAreaCode() + File.separator + fileName + ".jpg";
            }
            FileInputStream sheetIn = new FileInputStream(sheetPath);
            FileInputStream slicein = new FileInputStream(slicePath);
            String sheetMD5 = DigestUtils.md5Hex(sheetIn);
            String sliceMD5 = DigestUtils.md5Hex(slicein);
            paper.setSheetMD5(sheetMD5);
            paper.setSliceMD5(sliceMD5);
            sheetIn.close();
            slicein.close();
        }
        paperRepo.save(paper);
        synchronized (this) {
            String uploadStatus = student.getUploadStatus();
            String replace = subject.toString() + ":1";
            String regx = subject.toString() + ":[0-1]";
            String newStatus = uploadStatus.replaceFirst(regx, replace);
            student.setUploadStatus(newStatus);
            studentRepo.save(student);
        }
        return paper;
    }

    @Transactional
    public List<ExcelError> uploadStudentRelate(Long workId, InputStream inputStream) throws Exception {
        //已进入分档，不能导入
        List<MarkSubject> list = markSubjectRepo.findAllByWorkIdAndStageNot(workId, MarkStage.INIT);
        if (list != null && list.size() > 0) {
            throw new Exception("已进入分档或者打分阶段，不能导入关联考生数据");
        }

        ExcelReader excelReader = new ExcelReader(StudentRelateDTO.class);
        List<ExcelError> excelErrors = excelReader.reader(inputStream, obj -> {
            try {
                StudentRelateDTO dto = (StudentRelateDTO) obj;
                //校验excel文件中数据是否完整
                if (!checkObjFieldIsNotNull(dto)) {
                    throw new RuntimeException("考生信息缺失");
                }

                String examNumber = dto.getExamNumber();
                //如果没有设置区域代码，用准考证前2位作为区域代码
                String areaCode = null;
                if (dto.getAreaCode() == null || dto.getAreaCode().isEmpty()) {
                    areaCode = examNumber.substring(0, 2);
                    dto.setAreaCode(areaCode);
                }

                Student student = studentRepo.findByWorkIdAndExamNumberAndTest(workId, dto.getExamNumber(), String.valueOf(TrialEnum.DEFAULT.getId()));
                if (student == null) {
                    throw new RuntimeException("考生不存在");
                }
                //检验其它信息是否一致
                if (!Objects.equals(student.getName(), dto.getName())
                        || !Objects.equals(student.getAreaName(), dto.getAreaName())
                        || !Objects.equals(student.getExamRoom(), dto.getExamRoom())
                        || !Objects.equals(student.getSourceName(), dto.getSourceName())) {
                    throw new RuntimeException("考生信息与系统数据不匹配");
                }
                //设置关联考号
                student.setRelateExamNumber(dto.getRelateExamNumber());
                studentRepo.save(student);
                return null;
            } catch (RuntimeException e) {
                //手动回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                ExcelError excelError = new ExcelError();
                excelError.setExcelErrorType(e.getMessage());
                return excelError;
            }
        });
        String errors = errorsString(excelErrors);
        if(errors.length() > 0) {
            throw new Exception(errors);
        }
        return excelErrors;
    }

    /**
     * 获取随机号
     *
     * @param workId
     * @param examNumber
     * @return
     * @throws Exception
     */
    public Long getRandom(Long workId, String examNumber) throws Exception {
        int count = 0, result = 0;
        Long random = 0L;
        while (true) {
            random = randomUtil.getRandomMap().get(workId).get(new Random().nextInt(randomUtil.getRandomMap().get(workId).size()));
//            random = RandomUtil.randomList.get(new Random().nextInt(RandomUtil.randomList.size()));
            result = paperRepo.countByWorkIdAndExamNumberAndRandomSeq(workId, examNumber, random);
            if (result == 0 && random != Long.parseLong(examNumber.substring(3, examNumber.length()))) {
                break;
            } else {
                count++;
            }
            if (count > 1000) {
//                throw new Exception("重复几率较高，建议重新生成随机号");
                randomUtil.getRandom(workId, true);
                getRandom(workId, examNumber);
            }
        }
        return random;
    }

    /**
     * 校验学生导入所有属性
     *
     * @param obj
     * @return
     */
    private boolean checkObjFieldIsNotNull(Object obj) {
        for (Field field : obj.getClass().getDeclaredFields()) {
            if (Objects.equals("id", field.getName()) || Objects.equals("test", field.getName()) || Objects.equals("workId", field.getName())) {
                continue;
            }
            field.setAccessible(true);
            try {
                if (field.get(obj) == null) {
                    return false;
                }
            } catch (IllegalAccessException e) {
                return false;
            }
        }
        return true;
    }

    /**
     * 拼接导入异常信息
     * @param excelErrors
     * @return
     */
    private String errorsString(List<ExcelError> excelErrors) {
        StringJoiner sj = new StringJoiner(";");
        if(!excelErrors.isEmpty() && excelErrors.size() > 0){
            int forint = excelErrors.size() < 10 ? excelErrors.size() : 9 ;
            for (int i = 0; i < forint; i++) {
                ExcelError excelError = excelErrors.get(i);
                StringBuffer sb = new StringBuffer();
                sb.append("第").append(excelError.getRow()).append("行,").append(excelError.getExcelErrorType());
                sj.add(sb.toString());
            }
        }
        return sj.toString();
    }
}
