package cn.com.qmth.stmms.ms.collect.api;

import cn.com.qmth.stmms.ms.accesscontrol.config.LoginConfig;
import cn.com.qmth.stmms.ms.admin.service.DataUploadService;
import cn.com.qmth.stmms.ms.collect.dto.CollectStuDTO;
import cn.com.qmth.stmms.ms.collect.dto.CollectSubjectDTO;
import cn.com.qmth.stmms.ms.collect.dto.LoginDTO;
import cn.com.qmth.stmms.ms.collect.util.OssUploadUtil;
import cn.com.qmth.stmms.ms.commons.config.*;
import cn.com.qmth.stmms.ms.commons.constant.SystemConstant;
import cn.com.qmth.stmms.ms.commons.utils.*;
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.domain.user.MarkUser;
import cn.com.qmth.stmms.ms.core.domain.user.Role;
import cn.com.qmth.stmms.ms.core.repository.*;
import cn.com.qmth.stmms.ms.core.vo.Subject;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.assertj.core.util.Strings;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * Created by dizhi on 2016/12/24.
 */
@RestController
@RequestMapping("api")
public class CollectApi {
    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(CollectApi.class);

    private static final String SHEET = "SHEET", THUMB = "THUMB", IMAGE = "IMAGE";

    @Autowired
    private LoginConfig loginConfig;

    @Autowired
    private WorkRepo workRepo;

    @Autowired
    private StudentRepo studentRepo;

    @Autowired
    private MarkUserRepo markUserRepo;

    @Autowired
    private MarkSubjectRepo markSubjectRepo;

    @Autowired
    private ParamSettingRepo paramSettingRepo;

    @Autowired
    private DataUploadService dataUploadService;

    @Autowired
    private LevelRepo levelRepo;

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private SystemConfig systemConfig;

    @Autowired
    private ImageCompressionConfig compressionConfig;

    @Autowired
    ImageConfig imageConfig;

    @Resource
    AliYunOssConfig aliYunOssConfig;

    @Autowired
    SqlUtil sqlUtil;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    OssUploadUtil ossUploadUtil;

    @Autowired
    OssUtil ossUtil;

    @Autowired
    ImageServerConfig imageServerConfig;

    ExecutorService executors = Executors.newFixedThreadPool(10);

    @RequestMapping("user/login")
    public LoginDTO login(@RequestParam String loginname, @RequestParam String password) throws Exception {
        LoginDTO loginDTO = null;
        Work activeWork = null;
        /*if (loginname.equals(loginConfig.uploadLoginConfig().getLoginName()) && password.equals(loginConfig.uploadLoginConfig().getPassword())) {
            activeWork = workRepo.findByActiveTrue();
            if (Objects.isNull(activeWork)) {
                throw new Exception("没有work");
            }
            loginDTO = new LoginDTO();
            loginDTO.setExamId(activeWork.getId());
            loginDTO.setExamName(activeWork.getName());
        } else if (loginname.equals(loginConfig.adminLoginConfig().getLoginName()) && password.equals(loginConfig.adminLoginConfig().getPassword())) {
            activeWork = workRepo.findByActiveTrue();
            if (Objects.isNull(activeWork)) {
                throw new Exception("没有work");
            }
            loginDTO = new LoginDTO();
            loginDTO.setExamId(activeWork.getId());
            loginDTO.setExamName(activeWork.getName());
            loginDTO.setRoleCode("ADMIN");
        } else {*/
        MarkUser markUser = markUserRepo.findByLoginName(loginname);
        //账号不存在
        if (Objects.isNull(markUser)) {
            throw new RuntimeException("账号不存在");
        }
        if (!markUser.isEnabled()) {
            throw new RuntimeException("账号已禁用");
        }
        //不是采集员账号
        else if (!Objects.equals(Role.COLLECTOR, markUser.getRole())
                && !Objects.equals(Role.ADMIN, markUser.getRole())) {
            throw new RuntimeException("非采集端账号或者机构管理员账号");
        }
        //密码错误
        else if (!Objects.equals(markUser.getPassword(), password)) {
            throw new RuntimeException("密码错误");
        }
//            activeWork = workRepo.findByActiveTrue();
        activeWork = workRepo.findByActiveTrueAndOrganizationId(markUser.getOrganizationId());
        if (Objects.isNull(activeWork)) {
            throw new Exception("没有work");
        }

        //采集用户工作与当前工作不匹配
        if (Objects.equals(Role.COLLECTOR, markUser.getRole()) && markUser.getWorkId() != activeWork.getId()) {
            throw new RuntimeException("此采集账号与当前工作文件夹不匹配");
        }

        loginDTO = new LoginDTO();
        loginDTO.setOrganizationId(activeWork.getOrganizationId());
        loginDTO.setUserId(markUser.getId());
        loginDTO.setExamId(activeWork.getId());
        loginDTO.setExamName(activeWork.getName());
        loginDTO.setImageEnc(ParamCache.paramMap.get(activeWork.getId()).getImageEncrypt() == 1);
//        }
        List<CollectSubjectDTO> collectSubjectDTOs = new ArrayList<>();
        activeWork.getSubjects().forEach(s -> {
            int id = s.getSubject().ordinal() + 1;
            CollectSubjectDTO collectSubjectDTO = new CollectSubjectDTO(id, s.getName());
            if (!Strings.isNullOrEmpty(s.getCollectConfig())) {
                JSONObject json = JSONObject.fromObject(s.getCollectConfig());
                collectSubjectDTO.setCollectConfig(json);
            }
            collectSubjectDTOs.add(collectSubjectDTO);
        });
        loginDTO.setSubjects(collectSubjectDTOs);

        //采集参数
        ParamSetting paramSetting = paramSettingRepo.findByWorkId(activeWork.getId());
        loginDTO.setParamSetting(paramSetting);
        return loginDTO;
    }


    @RequestMapping(value = "exam/listStudents/{examNumber}", method = RequestMethod.GET)
    public List<CollectStuDTO> listStudents(HttpServletRequest request, @PathVariable String examNumber) {
        Long organizationId = Long.valueOf(request.getHeader("organizationId"));
        List<CollectStuDTO> list = new ArrayList<>();
//        Work activeWork = workRepo.findByActiveTrue();
        Work activeWork = workRepo.findByActiveTrueAndOrganizationId(organizationId);
        Consumer<Student> consumer = (s) -> {
            CollectStuDTO collectStuDTO = new CollectStuDTO();
            collectStuDTO.setExamId(activeWork.getId());
            collectStuDTO.setExamNumber(s.getRelateExamNumber());
            collectStuDTO.setName(s.getName());
            collectStuDTO.setSiteCode(s.getAreaName());
            collectStuDTO.setRoomCode(s.getExamRoom());
            list.add(collectStuDTO);
        };
        Student student = studentRepo.findByWorkIdAndRelateExamNumber(activeWork.getId(), examNumber);
        studentRepo.findByWorkIdAndTestAndExamRoom(activeWork.getId(), String.valueOf(TrialEnum.DEFAULT.getId()), student.getExamRoom()).forEach(consumer);
        return list;
    }

    @RequestMapping(value = "exam/getStudent/{examNumber}", method = RequestMethod.GET)
    public CollectStuDTO getStudent(HttpServletRequest request, @PathVariable String examNumber) {
        Long organizationId = Long.valueOf(request.getHeader("organizationId"));
//        Work activeWork = workRepo.findByActiveTrue();
        Work activeWork = workRepo.findByActiveTrueAndOrganizationId(organizationId);
        Student student = studentRepo.findByWorkIdAndRelateExamNumberAndTest(activeWork.getId(), examNumber, String.valueOf(TrialEnum.DEFAULT.ordinal()));
        if (student == null) {
            throw new RuntimeException("无该考生，请处理");
        }
        CollectStuDTO collectStuDTO = new CollectStuDTO();
        collectStuDTO.setExamId(activeWork.getId());
        collectStuDTO.setExamNumber(student.getRelateExamNumber());
        collectStuDTO.setName(student.getName());
        collectStuDTO.setSiteCode(student.getAreaName());
        collectStuDTO.setRoomCode(student.getExamRoom());
        return collectStuDTO;
    }


//    /**
//     * 获取客户端账户名和密码
//     *
//     * @return
//     */
//    @RequestMapping(value = "user/getClientUser", method = RequestMethod.GET)
//    public Map getClientUser() {
//        Map map = new HashMap();
//        map.put("clientUserName", loginConfig.clientLoginConfig().getLoginName());
//        map.put("clientUserPwd", loginConfig.clientLoginConfig().getPassword());
//        return map;
//    }

    @RequestMapping(value = "exam/students", method = RequestMethod.GET)
    public List<CollectStuDTO> getStudents(HttpServletRequest request) {
        List<CollectStuDTO> list = new ArrayList<>();
        Long organizationId = Long.valueOf(request.getHeader("organizationId"));
//        Work activeWork = workRepo.findByActiveTrue();
        Work activeWork = workRepo.findByActiveTrueAndOrganizationId(organizationId);
        Consumer<Student> consumer = (s) -> {
            CollectStuDTO collectStuDTO = new CollectStuDTO();
            collectStuDTO.setExamId(activeWork.getId());
            collectStuDTO.setExamNumber(s.getExamNumber());
            collectStuDTO.setName(s.getName());
            collectStuDTO.setSiteCode(s.getAreaName());
            collectStuDTO.setRoomCode(s.getExamRoom());
            list.add(collectStuDTO);
        };
        studentRepo.findByWorkIdAndTest(activeWork.getId(), String.valueOf(TrialEnum.DEFAULT.getId())).forEach(consumer);
        return list;
    }

    @CrossOrigin(maxAge = 3600) //支持跨域
    @RequestMapping(value = "upload/student/{subjectId}", method = RequestMethod.POST)
    public CollectStuDTO saveStudent(HttpServletRequest request, @PathVariable Integer subjectId,
                                     CollectStuDTO dto) throws Exception {
        Subject subject = Subject.values()[subjectId - 1];
        Long organizationId = Long.valueOf(request.getHeader("organizationId"));
//        Work activeWork = workRepo.findByActiveTrue();
        Work activeWork = workRepo.findByActiveTrueAndOrganizationId(organizationId);
        List<CollectStuDTO> list = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
//        for (CollectStuDTO dto : uploadStudentArray) {
        Student student = studentRepo.findByWorkIdAndRelateExamNumberAndTest(activeWork.getId(), dto.getExamNumber(), String.valueOf(TrialEnum.DEFAULT.getId()));
        Paper paper = null;
        if (!dto.isAbsent()) {
            paper = dataUploadService.savePaper(student, subject, dto.isManual(), dto.getLevel());
        }
        //已知档位，更新阶段
        if (Objects.nonNull(dto.getLevel()) && dto.getLevel() != "") {
            MarkSubject markSubject = markSubjectRepo.findOne(activeWork.getId() + "-" + subject.name());
            if (markSubject.getStage().name().equals(MarkStage.INIT.name())) {
                markSubject.setStage(MarkStage.LEVEL);
                markSubjectRepo.saveAndFlush(markSubject);
            }
        }
        dto.setUploadTime(sdf.format(new Date()));
        if (paper != null) {
            dto.setPaperId(paper.getId());
        }
        return dto;
    }

//    File srcFile = new File("/Users/king/stmms-ms/static/images/2/SM/1/1901040150.jpg"); //初始文件

    /**
     * 上传原图(图片加密)
     *
     * @param workId
     * @param subjectId
     * @param fileName
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "file/image/uploadsheet/{workId}/{subjectId}/{fileName}", method = {RequestMethod.POST})
    public void imageUploadSheet(@PathVariable Long workId, @PathVariable Integer subjectId,
                                 @PathVariable String fileName,
                                 MultipartFile file,
                                 HttpServletRequest request, HttpServletResponse response) throws IOException {
        long start = System.currentTimeMillis();
        LOGGER.info("准备生成原图:{}", start);
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = file.getInputStream();
//            inputStream = new FileInputStream(srcFile);
            Student student = studentRepo.findByWorkIdAndRelateExamNumberAndTest(workId, fileName, String.valueOf(TrialEnum.DEFAULT.getId()));
            Subject subject = Subject.values()[subjectId - 1];

            //保存原图
            String sheetDir = systemConfig.getSheetDir() + File.separator + workId + File.separator + subject
                    + File.separator + student.getAreaCode();
            File out = new File(sheetDir);
            if (!out.exists()) {
                out.mkdirs();
            }
            String imageMd5 = MD5Util.getImageRuleMd5(workId, subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
            File sheetFile = new File(sheetDir + File.separator + imageMd5 + ".jpg");
            LOGGER.info("sheet存放目录:{}", sheetFile.getPath());
            outputStream = new FileOutputStream(sheetFile);
//            SystemConstant.writeStream(inputStream, outputStream);
            byte[] bytes = new byte[1024 * 8];
            byte[] bytesEnc = new byte[1024 * 8];
            int index = 0;
            MessageDigest MD5 = MessageDigest.getInstance("MD5");//MD5值
            while ((index = inputStream.read(bytes)) != -1) {
                //将字节数组的数据全部写入到输出流中
                for (int i = 0; i < index; i++) {
                    //通过异或运算加密
                    bytesEnc[i] = (byte) (bytes[i] ^ SystemConstant.SECRET_KEY);
                }
                outputStream.write(bytesEnc, 0, index);
                MD5.update(bytes, 0, index);
            }
            long end = System.currentTimeMillis();
            LOGGER.info("生成原图耗时：{}", (end - start) / 1000 + "s");

            String md5 = request.getHeader("md5");
//            FileInputStream in = new FileInputStream(sheetFile);
            String sheetMD5 = new String(Hex.encodeHex(MD5.digest()));
            LOGGER.info("request里的MD5,生成的MD5:{}", md5, sheetMD5);
            if (!md5.equalsIgnoreCase(sheetMD5)) {
                throw new Exception("图片md5值不一致");
            }
//            ossUploadUtil.ossUpload(student, subject, sheetFile, SHEET, systemConfig.getSheetDir(), sheetMD5);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(inputStream)) {
                inputStream.close();
            }
            if (Objects.nonNull(outputStream)) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }

    /**
     * 上传裁切原图和缩略图到服务器(图片加密)
     *
     * @param workId
     * @param subjectId
     * @param fileName
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "file/image/upload/{workId}/{subjectId}/{fileName}", method = {RequestMethod.POST})
    public void imageUpload(@PathVariable Long workId, @PathVariable Integer subjectId,
                            @PathVariable String fileName,
                            MultipartFile file,
                            HttpServletRequest request, HttpServletResponse response) throws IOException {
        long start = System.currentTimeMillis();
        LOGGER.info("准备生成裁切原图和缩略图:{}", start);
        File imageFileTemp = null;
        try {
            InputStream inputStream = file.getInputStream();
//            inputStream = new FileInputStream(srcFile);
            Student student = studentRepo.findByWorkIdAndRelateExamNumberAndTest(workId, fileName, String.valueOf(TrialEnum.DEFAULT.getId()));
            Subject subject = Subject.values()[subjectId - 1];
            //保存裁切原图+文件名加密
            String imageDir = systemConfig.getImageDir() + File.separator + workId + File.separator + subject
                    + File.separator + student.getAreaCode();
            File out = new File(imageDir);
            if (!out.exists()) {
                out.mkdirs();
            }

            String imageMd5 = MD5Util.getImageRuleMd5(workId, 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 * 8];
            byte[] bytesEnc = new byte[1024 * 8];
            MessageDigest MD5 = MessageDigest.getInstance("MD5");//MD5值
            while ((index = inputStream.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);
                MD5.update(bytes, 0, index);
            }
            inputStream.close();
            outputStream.flush();
            outputStream.close();
            outputStreamTemp.flush();
            outputStreamTemp.close();

            //生成缩略图
            String thumbDir = systemConfig.getThumbDir() + File.separator + workId + 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");
//            File thumbFileTemp = new File(thumbDir + File.separator + imageMd5 + "temp.jpg");
            LOGGER.info("thumb存放目录:{}", thumbFile.getPath());
            OutputStream outputStream1 = new FileOutputStream(thumbFile);
//            outputStreamTemp = new FileOutputStream(thumbFileTemp);
//            ImageIO.write(bufferedImage, "jpg", outputStreamTemp);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "jpg", os);
            InputStream inputStream1 = new ByteArrayInputStream(os.toByteArray());

//            inputStream = new FileInputStream(thumbFileTemp);
            //缩略图删除
//            thumbFileTemp.delete();
            SystemConstant.writeStream(inputStream1, outputStream1);

//            String thumbMD5 = DigestUtils.md5Hex(inputStream1);
            long end = System.currentTimeMillis();
            LOGGER.info("生成原图和缩略图耗时：{}", (end - start) / 1000 + "s");

            inputStream1.close();
            outputStream1.flush();
            outputStream1.close();
            String md5 = request.getHeader("md5");
//            FileInputStream in = new FileInputStream(imageFileTemp);
            String sliceMD5 = new String(Hex.encodeHex(MD5.digest()));
            LOGGER.info("request里的MD5,生成的MD5:{}", md5, sliceMD5);
            if (!md5.equalsIgnoreCase(sliceMD5)) {
                throw new Exception("图片md5值不一致");
            }
            /*if(imageFile.exists()){
                ossUploadUtil.ossUpload(student, subject, imageFile ,IMAGE, systemConfig.getImageDir(),sliceMD5);
            }
            if(thumbFile.exists()){
                ossUploadUtil.ossUpload(student, subject, thumbFile, THUMB, systemConfig.getThumbDir(), thumbMD5);
            }*/
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //原图删除
            if (Objects.nonNull(imageFileTemp) && imageFileTemp.exists()) {
                imageFileTemp.delete();
            }
        }
    }

    /**
     * 从服务器上下载图片(图片解密)
     *
     * @param workId
     * @param subjectId
     * @param studentId
     * @param imageType
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "file/image/download/{workId}/{subjectId}/{studentId}/{imageType}", method = {RequestMethod.GET})
    public void imageDownload(@PathVariable Long workId, @PathVariable Integer subjectId,
                              @PathVariable Long studentId, @PathVariable Integer imageType,
                              HttpServletRequest request, HttpServletResponse response) throws IOException {
        long start = System.currentTimeMillis();
        LOGGER.info("准备读取图片:{},imageType:{}", start, imageType);
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            response.setContentType("image/jpg");
            Student student = studentRepo.findOne(studentId);
            Subject subject = Subject.values()[subjectId];
            String path = null;
            if (imageServerConfig.isAliyunOss()) {
                String dir;
                switch (imageType) {
                    case 1:
                        //裁切原图
                        dir = systemConfig.getImageDir();
                        break;
                    case 2:
                        //缩略图
                        dir = systemConfig.getThumbDir();
                        break;
                    case 3:
                        //原图
                        dir = systemConfig.getSheetDir();
                        break;
                    default:
                        //分数水印图
                        dir = "";
                        break;
                }
                StringJoiner ossSavePath = new StringJoiner("/").add(imageServerConfig.getDir()).add(dir.replace("\\", "/")).add(String.valueOf(student.getWorkId())).add(subject.name()).add(student.getAreaCode());
                String md5 = MD5Util.getImageRuleMd5(workId, subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
                String ossFilePath = ossSavePath.toString().concat("/").concat(md5).concat(".jpg");
                inputStream = ossUploadUtil.ossDownload(ossFilePath);
            } else {
                switch (imageType) {
                    case 1:
                        //裁切原图
                        path = systemConfig.getImageDir() + File.separator + workId + File.separator + subject
                                + File.separator + student.getAreaCode();
                        break;
                    case 2:
                        //缩略图
                        path = systemConfig.getThumbDir() + File.separator + workId + File.separator + subject
                                + File.separator + student.getAreaCode();
                        break;
                    case 3:
                        //原图
                        path = systemConfig.getSheetDir() + File.separator + workId + File.separator + subject
                                + File.separator + student.getAreaCode();
                        break;
                    default:
                        //分数水印图
                        path = systemConfig.getWatermark() + File.separator + workId + File.separator + subject
                                + File.separator + student.getAreaCode();
                        break;
                }
                String md5 = MD5Util.getImageRuleMd5(workId, subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
                File file = new File(path + File.separator + md5 + ".jpg");
                //读取指定路径下面的文件
                inputStream = new FileInputStream(file);
            }
            outputStream = new BufferedOutputStream(response.getOutputStream());
            SystemConstant.writeStream(inputStream, outputStream);
            long end = System.currentTimeMillis();
            LOGGER.info("读取图片耗时：{}", (end - start) / 1000 + "s");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //强制将缓存区的数据进行输出
            if (Objects.nonNull(outputStream)) {
                outputStream.flush();
                //关流
                outputStream.close();
            }
            if (Objects.nonNull(inputStream)) {
                inputStream.close();
            }
        }
    }

    /**
     * 上传裁切原图并压缩
     *
     * @param workId
     * @param subjectId
     * @param file
     * @param request
     * @param response
     * @throws Exception
     */
    @CrossOrigin(maxAge = 3600) //支持跨域
    @RequestMapping(value = "file/ms-slice/{workId}/{subjectId}/{examNumber}", method = {RequestMethod.PUT, RequestMethod.POST})
    public void upload(@PathVariable Long workId, @PathVariable Integer subjectId,
                       @PathVariable String examNumber,
                       @RequestParam MultipartFile file,
                       HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (Objects.isNull(file)) {
            throw new Exception("file文件不能为空");
        }
        int temp = file.getOriginalFilename().indexOf(".");
//        String examNumber = file.getOriginalFilename().substring(0, temp);
        String format = file.getOriginalFilename().substring(temp, file.getOriginalFilename().length());
        if (Objects.nonNull(format) && !(format.equalsIgnoreCase(".jpg") || format.equalsIgnoreCase(".jpeg") || format.equalsIgnoreCase(".png"))) {
            throw new Exception("file文件格式只能为jpg,jpeg,png");
        }
        InputStream inputStream = file.getInputStream();
        Student student = studentRepo.findByWorkIdAndRelateExamNumberAndTest(workId, examNumber, String.valueOf(TrialEnum.DEFAULT.getId()));
        if (Objects.isNull(student)) {
            throw new Exception("准考证号与学生不匹配");
        }
        Subject subject = Subject.values()[subjectId - 1];

        //保存原图
//        String saveSheetPath = systemConfig.getSheetDir() + File.separator + workId + File.separator + subject
//                + File.separator + student.getAreaCode();
        String savePath = systemConfig.getImageDir() + File.separator + workId + File.separator + subject
                + File.separator + student.getAreaCode();
        File imageOut = new File(savePath);
        if (!imageOut.exists()) {
            imageOut.mkdirs();
        }
//        File sheetOut = new File(saveSheetPath);
//        if (!sheetOut.exists()) {
//            sheetOut.mkdirs();
//        }
        String fileName = student.getExamNumber();
        //0：随机码 1：考号
        if (ParamCache.paramMap.get(student.getWorkId()).getNameRule() == 1) {
//            fileName = DigestUtils.md5Hex(subject.name() + fileName);
            fileName = MD5Util.getImageRuleMd5(workId, subject.ordinal(), student.getAreaCode(), student.getExamNumber(), student.getId());
        }
//        File outSheetFile = new File(saveSheetPath + File.separator + fileName + ".jpg");
        File outImageFile = new File(savePath + File.separator + fileName + ".jpg");
//        OutputStream outputSheetStream = new FileOutputStream(outSheetFile);
        OutputStream outputImageStream = new FileOutputStream(outImageFile);
        int index = 0;
        byte[] bytes = new byte[1024 * 8];
        while ((index = inputStream.read(bytes)) != -1) {
//            outputSheetStream.write(bytes, 0, index);
            outputImageStream.write(bytes, 0, index);
        }
        inputStream.close();
//        outputSheetStream.flush();
//        outputSheetStream.close();
        outputImageStream.flush();
        outputImageStream.close();

        //保存遮盖图
//        String savePath = systemConfig.getImageDir() + File.separator + workId + File.separator + subject
//                + File.separator + student.getAreaCode();
//        File outFile = saveImage(student, inputStream, savePath);

        // 生成缩略图
        String thumbDir = systemConfig.getThumbDir() + File.separator + workId + File.separator + subject
                + File.separator + student.getAreaCode();
        File thumb = new File(thumbDir);
        if (!thumb.exists()) {
            thumb.mkdirs();
        }
        BufferedImage bufferedImage = ImageCompression.compress(outImageFile, compressionConfig);
        String thumbFileName = thumbDir + File.separator + fileName + ".jpg";
        File outThumbFile = new File(thumbFileName);
        ImageIO.write(bufferedImage, "jpg", outThumbFile);
        FileInputStream thumbIn = new FileInputStream(outThumbFile);
        String thumbMD5 = DigestUtils.md5Hex(thumbIn);
        thumbIn.close();

        String md5 = request.getHeader("md5");
        if (Objects.isNull(md5)) {
            throw new Exception("request的md5为空");
        }
        FileInputStream in = new FileInputStream(outImageFile);
        String sliceMD5 = DigestUtils.md5Hex(in);
        if (!md5.equalsIgnoreCase(sliceMD5)) {
            throw new Exception("图片md5值不一致");
        }
        in.close();

        if (outImageFile.exists()) {
            ossUploadUtil.ossUpload(student, subject, outImageFile, IMAGE, systemConfig.getImageDir(), sliceMD5);
        }

        if (outThumbFile.exists()) {
            ossUploadUtil.ossUpload(student, subject, outThumbFile, THUMB, systemConfig.getThumbDir(), thumbMD5);
        }
//        ImageCompression.imageThumbnail(outFile,new File(thumbFileName),compressionConfig);
    }

    private File saveImage(Student student, InputStream inputStream,
                           String savePath, Subject subject) throws IOException, FileNotFoundException {
        File out = new File(savePath);
        if (!out.exists()) {
            out.mkdirs();
        }
        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());
        }
        File outFile = new File(savePath + File.separator + fileName + ".jpg");
        FileCopyUtils.copy(inputStream, new FileOutputStream(outFile));
        return outFile;
    }

    /**
     * 上传裁切原图并压缩
     *
     * @param workId
     * @param subjectId
     * @param file
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "file/ms-sheet/{workId}/{subjectId}/{fileName}", method = {RequestMethod.PUT, RequestMethod.POST})
    public void uploadsheet(@PathVariable Long workId, @PathVariable Integer subjectId,
                            @RequestParam MultipartFile file,
                            HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (Objects.isNull(file)) {
            throw new Exception("file文件不能为空");
        }
        int temp = file.getOriginalFilename().indexOf(".");
        String examNumber = file.getOriginalFilename().substring(0, temp);
        String format = file.getOriginalFilename().substring(temp, file.getOriginalFilename().length());
        if (Objects.nonNull(format) && !(format.equalsIgnoreCase(".jpg") || format.equalsIgnoreCase(".jpeg") || format.equalsIgnoreCase(".png"))) {
            throw new Exception("file文件格式只能为jpg,jpeg,png");
        }
        Student student = studentRepo.findByWorkIdAndRelateExamNumberAndTest(workId, examNumber, String.valueOf(TrialEnum.DEFAULT.getId()));
        if (Objects.isNull(student)) {
            throw new Exception("准考证号与学生不匹配");
        }
        Subject subject = Subject.values()[subjectId - 1];
        InputStream inputStream = file.getInputStream();
        //保存原图图
        String savePath = systemConfig.getSheetDir() + File.separator + workId + File.separator + subject
                + File.separator + student.getAreaCode();
        File outFile = saveImage(student, inputStream, savePath, subject);

        String md5 = request.getHeader("md5");
        if (Objects.isNull(md5)) {
            throw new Exception("request的md5为空");
        }
        FileInputStream in = new FileInputStream(outFile);
        String sheetMD5 = DigestUtils.md5Hex(in);
        if (!md5.equalsIgnoreCase(sheetMD5)) {
            throw new Exception("图片md5值不一致");
        }
        in.close();
        ossUploadUtil.ossUpload(student, subject, outFile, SHEET, systemConfig.getSheetDir(), sheetMD5);
    }

    @RequestMapping(value = "subject/collect-config", method = RequestMethod.POST)
    public boolean updateConfig(HttpServletRequest request, @RequestBody CollectSubjectDTO collectSubjectDTO) {
        Subject subject = Subject.values()[collectSubjectDTO.getSubjectId() - 1];
        Long organizationId = Long.valueOf(request.getHeader("organizationId"));
//        Work activeWork = workRepo.findByActiveTrue();
        Work activeWork = workRepo.findByActiveTrueAndOrganizationId(organizationId);
        MarkSubject markSubject = markSubjectRepo.findOne(activeWork.getId() + "-" + subject.toString());
        String config = Optional.ofNullable(collectSubjectDTO.getCollectConfig())
                .map(Object::toString)
                .map(s -> s.substring(1, s.length() - 1))
                .map(s -> s.replaceAll("\\\\", "")).get();
        markSubject.setCollectConfig(config);
        markSubjectRepo.save(markSubject);
        return true;
    }

    /**
     * 执行oss脚本上传
     *
     * @param workId
     * @return
     */
    @CrossOrigin(maxAge = 3600) //支持跨域
    @RequestMapping(value = "oss/upload/{workId}", method = RequestMethod.GET)
    public boolean ossUpload(@PathVariable Long workId) {
        try {
            File imageFile = new File(systemConfig.getImageDir() + File.separator + workId);
            if (Objects.nonNull(imageFile) && imageFile.exists()) {
                String imageResult = CommandUtil.run(new StringJoiner("").add(aliYunOssConfig.getUtil()).add(" ").add(systemConfig.getImageDir()).add(" ").add(aliYunOssConfig.getImageDir()).toString());
                LOGGER.info("ossUpload imageResult:{}", imageResult);
                FileUtil.deleteFile(imageFile);
            }
            File thumbFile = new File(systemConfig.getThumbDir() + File.separator + workId);
            if (Objects.nonNull(thumbFile) && thumbFile.exists()) {
                String thumbResult = CommandUtil.run(new StringJoiner("").add(aliYunOssConfig.getUtil()).add(" ").add(systemConfig.getThumbDir()).add(" ").add(aliYunOssConfig.getThumbDir()).toString());
                LOGGER.info("ossUpload thumbResult:{}", thumbResult);
                FileUtil.deleteFile(thumbFile);
            }
            File sheetFile = new File(systemConfig.getSheetDir() + File.separator + workId);
            if (Objects.nonNull(sheetFile) && sheetFile.exists()) {
                String sheetResult = CommandUtil.run(new StringJoiner("").add(aliYunOssConfig.getUtil()).add(" ").add(systemConfig.getSheetDir()).add(" ").add(aliYunOssConfig.getSheetDir()).toString());
                LOGGER.info("ossUpload sheetResult:{}", sheetResult);
                FileUtil.deleteFile(sheetFile);
            }
            File watermarkFile = new File(systemConfig.getWatermark() + File.separator + workId);
            if (Objects.nonNull(watermarkFile) && watermarkFile.exists()) {
                String watermarkResult = CommandUtil.run(new StringJoiner("").add(aliYunOssConfig.getUtil()).add(" ").add(systemConfig.getWatermark()).add(" ").add(aliYunOssConfig.getWatermark()).toString());
                LOGGER.info("ossUpload watermarkResult:{}", watermarkResult);
                FileUtil.deleteFile(watermarkFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    /**
     * 查询档位
     *
     * @param workId
     * @return
     */
    @RequestMapping(value = "level/{workId}", method = RequestMethod.GET)
    public List<String> updateConfig(@PathVariable Long workId) {
        List<Level> levels = levelRepo.findByWorkId(workId);
        List<String> codes = levels.stream().map(m -> m.getCode()).collect(Collectors.toList());
        return codes;
    }


    /**
     * 分数图片导出
     *
     * @param imageType
     * @param nameRule   1:考号+姓名，2：流水号
     * @param startScore
     * @param endScore
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "file/image/exportScorePictures", method = {RequestMethod.GET})
    public void exportScorePictures(@RequestParam(defaultValue = "score-images") String exportType,
                                    @RequestParam Long workId,
                                    @RequestParam String imageType,
                                    @RequestParam String isWatermark,
                                    @RequestParam String isResume,
                                    @RequestParam String nameRule,
                                    @RequestParam(required = false) Integer startScore,
                                    @RequestParam(required = false) Integer endScore,
                                    @RequestParam String savePath) throws Exception {

        if (StringUtils.isBlank(imageType)) {
            throw new RuntimeException("请选择图片类型");
        }
        if (StringUtils.isBlank(nameRule)) {
            throw new RuntimeException("请选择命名规则");
        }
        if (StringUtils.isBlank(savePath)) {
            throw new RuntimeException("请输入文档路径");
        }

        String[] strings = {"{{workId}}", "{{areaCode}}", "{{school}}", "{{subject}}"};
        List<String> stringList = Arrays.asList(strings);
        String[] savePaths = savePath.split("\\\\");
        for (String string : savePaths) {
            if (StringUtils.isEmpty(string)) {
                continue;
            }
            if (!stringList.contains(string)) {
                throw new Exception("文档路径格式不对");
            }
        }

        try {
            //首先根据workId和阶段去查所有试卷
            StringBuffer sql = new StringBuffer();
            sql.append("SELECT s.id studentId,s.name studentName,s.school, p.work_id workId, p.subject, p.area_code areaCode, p.exam_number examNumber, 0+cast(p.score as char) as score FROM paper p LEFT JOIN student s ON p.work_id = s.work_id AND p.exam_number = s.exam_number WHERE p.score is not null and p.work_id = ").append(workId);
            if (startScore != null && endScore != null) {
                sql.append(" and p.score >= ").append(startScore);
                sql.append(" and p.score <= ").append(endScore);
            }
            if (startScore != null && endScore == null) {
                sql.append(" and p.score = ").append(startScore);
            }
            if (startScore == null && endScore != null) {
                sql.append(" and p.score = ").append(endScore);
            }
            List<Map<String, Object>> paperList = jdbcTemplate.queryForList(sql.toString());
            AtomicInteger atomicInteger = new AtomicInteger(0);
            LOGGER.info("==========================导出分数图片开始，共{}数据================================", paperList.size());
            for (Map map : paperList) {
                executors.submit(() -> {
                    synchronized (map.get("examNumber")) {
//                    synchronized (this) {
                        InputStream inputStream = null;
                        OutputStream outputStream = null;
                        long start = System.currentTimeMillis();
                        try {
                            Long studentId = Long.valueOf(map.get("studentId").toString());
                            String studentName = map.get("studentName").toString();
                            String subject = map.get("subject").toString();
                            Subject subject1 = Subject.valueOf(subject);
                            String areaCode = map.get("areaCode").toString();
                            String school = String.valueOf(map.get("school"));
                            String examNumber = map.get("examNumber").toString();
                            Double score = Double.parseDouble(map.get("score").toString());

                            String fileName = examNumber;
                            //图片加密、命名规则为随机码的，都是需要md5
                            if (ParamCache.paramMap.get(workId).getNameRule() == 1 || ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                                fileName = MD5Util.getImageRuleMd5(workId, subject1.ordinal(), areaCode, examNumber, studentId);
                            }

                            //组装导出目录
                            StringJoiner expSj = new StringJoiner(File.separator);
                            expSj.add(systemConfig.getLocalhostPath()).add("image-export").add(exportType);
                            //原图或者裁切图
                            String imageTypeStr = Objects.equals("1", imageType) ? "sheet" : Objects.equals("2", imageType) ? "images" : "default";
                            //是否水印
                            String waterTypeStr = Objects.equals("1", isWatermark) ? "yes" : Objects.equals("0", isWatermark) ? "no" : "default";
                            expSj.add(imageTypeStr.concat("-").concat(waterTypeStr));
                            LOGGER.info("【{}，{}】开始导出，参数：图片类型：{}，是否有水印：{}，是否续传：{},命名规则：{}", examNumber, studentName, imageTypeStr, waterTypeStr, Objects.equals(isResume, "1") ? "是" : "否", Objects.equals(nameRule, "1") ? "考号+姓名" : "流水号");
                            //是否加水印
                            for (String spath : savePaths) {
                                if (stringPath("workId").equals(spath)) {
                                    expSj.add(String.valueOf(workId));
                                }
                                if (stringPath("areaCode").equals(spath)) {
                                    expSj.add(areaCode);
                                }
                                if (stringPath("school").equals(spath)) {
                                    expSj.add(school);
                                }
                                if (stringPath("subject").equals(spath)) {
                                    expSj.add(subject);
                                }
                            }
                            File out = new File(expSj.toString());
                            if (!out.exists()) {
                                out.mkdirs();
                            }
                            //输出命名
                            String outFileName;
                            if (Objects.equals("1", nameRule)) {
                                StringJoiner sj = new StringJoiner("-");
                                sj.add(examNumber).add(studentName);
                                outFileName = sj.toString();
                            } else if (Objects.equals("2", nameRule)) {
                                outFileName = String.valueOf(atomicInteger.incrementAndGet());
                            } else {
                                throw new RuntimeException("命名参数有误");
                            }

                            String path;
                            String dir;
                            if (Objects.equals("1", imageType)) {
                                path = systemConfig.getSheetDir() + File.separator + workId + File.separator + subject1
                                        + File.separator + areaCode;
                                dir = systemConfig.getSheetDir();
                            } else if (Objects.equals("2", imageType)) {
                                path = systemConfig.getImageDir() + File.separator + workId + File.separator + subject1
                                        + File.separator + areaCode;
                                dir = systemConfig.getImageDir();
                            } else {
                                throw new RuntimeException("图片类型有误");
                            }

                            String outFile = expSj.toString() + File.separator + outFileName + ".jpg";
                            if (imageServerConfig.isAliyunOss()) {
                                //是否断点续传
                                if (Objects.equals("1", isResume) && new File(outFile).exists()) {
                                    return;
                                }
                                StringJoiner ossSavePath = new StringJoiner("/").add(imageServerConfig.getDir()).add(dir.replace("\\", "/")).add(String.valueOf(workId)).add(subject).add(areaCode);
                                String saveFileName = fileName + ".jpg";
                                String saveFile = ossSavePath.toString().concat("/").concat(saveFileName);
                                if (Objects.equals("0", isWatermark)) {
                                    ossUtil.ossDownload(saveFile, outFile);
                                }
                                //下载临时原图
                                if (Objects.equals("1", isWatermark)) {
                                    long s1 = System.currentTimeMillis();
                                    LOGGER.info("【{}，{}】下载临时原图开始", examNumber, studentName);
                                    String outSheetFile = expSj.toString() + File.separator + outFileName + "_" + start + ".jpg";
                                    File sheetFileTemp = ossUtil.ossDownload(saveFile, outSheetFile);
                                    long s2 = System.currentTimeMillis();
                                    LOGGER.info("【{}，{}】下载临时原图结束，总耗时：{}秒", examNumber, studentName, (s2 - s1) / 1000);
                                    File expFile = new File(outFile);
                                    //生成分数水印图片
                                    long s3 = System.currentTimeMillis();
                                    LOGGER.info("【{}，{}】加水印开始，总耗时：{}秒", examNumber, studentName, (s3 - s2) / 1000);
                                    BufferedImage watermarkImage = createWaterImage(score.intValue() + "分");
                                    Thumbnails.of(sheetFileTemp).scale(1f).outputQuality(0.98f).watermark(Positions.BOTTOM_CENTER, watermarkImage, 1f).toFile(expFile);
                                    long s4 = System.currentTimeMillis();
                                    LOGGER.info("【{}，{}】加水印结束，总耗时：{}秒", examNumber, studentName, (s4 - s3) / 1000);
                                    sheetFileTemp.delete();
                                }
                            } else {
                                File file = new File(path + File.separator + fileName + ".jpg");
                                File expFile = new File(outFile);
                                //是否断点续传
                                if (Objects.equals("1", isResume) && expFile.exists()) {
                                    return;
                                }
                                if (Objects.equals("0", isWatermark)) {
                                    //读取指定路径下面的文件
                                    inputStream = new FileInputStream(file);
                                    outputStream = new FileOutputStream(expFile);
                                    if (ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                                        outputStream = SystemConstant.writeStream(inputStream, outputStream);
                                    } else {
                                        outputStream = SystemConstant.writeStreamFomal(inputStream, outputStream);
                                    }
                                    inputStream.close();
                                    outputStream.flush();
                                    outputStream.close();
                                }
                                if (Objects.equals("1", isWatermark)) {
                                    //生成分数水印图片
                                    BufferedImage watermarkImage = createWaterImage(score.intValue() + "分");
                                    Thumbnails.of(file).scale(1f).outputQuality(0.98f).watermark(Positions.BOTTOM_CENTER, watermarkImage, 1f).toFile(expFile);
                                }
                            }
                            long end = System.currentTimeMillis();
                            LOGGER.info("【{}，{}】结束导出，总耗时：{}秒", examNumber, studentName, (end - start) / 1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                            LOGGER.info("导出失败：{}", e.getMessage());
                        } finally {
                            try {
                                //强制将缓存区的数据进行输出
                                if (Objects.nonNull(outputStream)) {
                                    outputStream.flush();
                                    //关流
                                    outputStream.close();
                                }
                                if (Objects.nonNull(inputStream)) {
                                    inputStream.close();
                                }

                            } catch (Exception e) {

                            }
                        }
                    }
                });

            }
            LOGGER.info("==========================导出分数图片结束================================");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 图片解密、重命名导出
     *
     * @param imageType
     * @param areaId
     * @param subject
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "file/image/exportDecryptPictures", method = {RequestMethod.GET})
    public String exportDecryptPictures(@RequestParam(defaultValue = "图片解密") String exportType,
                                        @RequestParam Long workId,
                                        @RequestParam String imageType,
                                        @RequestParam Long areaId,
                                        @RequestParam String subject) throws IOException {
        if (StringUtils.isBlank(imageType)) {
            throw new RuntimeException("请选择图片类型");
        }
        if (areaId == null) {
            throw new RuntimeException("请选择考点");
        }
        if (StringUtils.isBlank(subject)) {
            throw new RuntimeException("请选择科目");
        }

        InputStream inputStream = null;
        OutputStream outputStream = null;
        int count = 0;
        try {
            //首先根据workId和阶段去查所有试卷
            StringBuffer sql = new StringBuffer();
            sql.append("SELECT s.id studentId, p.work_id workId, p.subject, p.area_code areaCode, p.exam_number examNumber FROM paper p LEFT JOIN student s ON p.work_id = s.work_id AND p.exam_number = s.exam_number WHERE p.score is not null and p.work_id = ").append(workId).append(" AND p.subject = '").append(subject).append("'");
            if (areaId != null) {
                sql.append(" and p.area_code = ").append(areaId);
            }
            List<Map<String, Object>> paperList = jdbcTemplate.queryForList(sql.toString());
            for (Map map : paperList) {
                Long studentId = Long.valueOf(map.get("studentId").toString());
                String subject0 = map.get("subject").toString();
                Subject subject1 = Subject.valueOf(subject0);
                String areaCode = map.get("areaCode").toString();
                String examNumber = map.get("examNumber").toString();

                String fileName = examNumber;
                //图片加密、命名规则为随机码的，都是需要md5
                if (ParamCache.paramMap.get(workId).getNameRule() == 1 || ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                    fileName = MD5Util.getImageRuleMd5(workId, subject1.ordinal(), areaCode, examNumber, studentId);
                }
                String path;
                if (Objects.equals("1", imageType)) {
                    path = systemConfig.getImageDir() + File.separator + workId + File.separator + subject1
                            + File.separator + areaCode;
                } else if (Objects.equals("2", imageType)) {
                    path = systemConfig.getImageDir() + File.separator + workId + File.separator + subject1
                            + File.separator + areaCode;
                } else {
                    throw new Exception("图片类型有误");
                }
                File file = new File(path + File.separator + fileName + ".jpg");
                //组装导出目录
                StringJoiner expSj = new StringJoiner(File.separator);
                expSj.add(systemConfig.getLocalhostPath()).add("图片导出").add(exportType).add(String.valueOf(workId)).add(subject);
                if (areaId != null) {
                    expSj.add(String.valueOf(areaId));
                }
                //原图或者裁剪图
                if (Objects.equals("1", imageType)) {
                    expSj.add("sheet");
                } else if (Objects.equals("2", imageType)) {
                    expSj.add("images");
                }
                File out = new File(expSj.toString());
                if (!out.exists()) {
                    out.mkdirs();
                }
                File expFile = new File(expSj.toString() + File.separator + examNumber + ".jpg");
                //水印图片
                //读取指定路径下面的文件
                inputStream = new FileInputStream(file);
                outputStream = new FileOutputStream(expFile);
                if (ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                    outputStream = SystemConstant.writeStream(inputStream, outputStream);
                } else {
                    outputStream = SystemConstant.writeStreamFomal(inputStream, outputStream);
                }
                inputStream.close();
                outputStream.flush();
                outputStream.close();
                count++;

                LOGGER.info("studentId:{},subjdect:{},md5:{}", studentId, subject1, fileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //强制将缓存区的数据进行输出
            if (Objects.nonNull(outputStream)) {
                outputStream.flush();
                //关流
                outputStream.close();
            }
            if (Objects.nonNull(inputStream)) {
                inputStream.close();
            }
        }
        return "导出照片成功,共导出了" + count + "张照片";
    }


    /**
     * 生成分数水印图片
     *
     * @param str
     * @return
     */
    public static BufferedImage createWaterImage(String str) {
        try {
            int width = 220;
            int height = 220;
            Font font = new Font("宋体", Font.BOLD, 100);
            //创建BufferedImage对象
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            //在背景图片中添加入需要写入的信息，
            Graphics2D g = image.createGraphics();

            //设置透明  end
            g.setBackground(Color.WHITE);
            g.clearRect(0, 0, width, height);
            g.setPaint(new Color(255, 0, 0, 0));      //设置背景颜色
            g.fillRect(0, 0, width, height);  //填充颜色

            //设置透明  start
            image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
            g = image.createGraphics();

            //设置字体颜色
            g.setColor(Color.red);
            g.setFont(font);
            g.drawString(str, 0, 100);
            g.dispose();
            return image;
        } catch (Exception e) {
            throw new RuntimeException("生成分数水印失败");
        }
    }

    private String stringPath(String s) {
        StringJoiner stringJoiner = new StringJoiner("", "{{", "}}");
        stringJoiner.add(s);
        return stringJoiner.toString();
    }

    //客户端图片导出，查询需要导出试卷
    @RequestMapping("/file/image/getExportData")
    public List<Map<String, Object>> getExportData(@RequestParam Long workId,
                                                   @RequestParam String imageType,
                                                   @RequestParam(required = false) Integer startScore,
                                                   @RequestParam(required = false) Integer endScore) {
        StringBuffer sql = new StringBuffer();
        sql.append("SELECT s.id studentId,s.name studentName,s.school, p.work_id workId, p.subject, p.area_code areaCode,s.area_name areaName, p.exam_number examNumber,ms.name subjectName, 0+cast(p.score as char) as score FROM paper p LEFT JOIN student s ON p.work_id = s.work_id AND p.exam_number = s.exam_number left join mark_subject ms on concat(p.work_id,'-', p.subject) = ms.id WHERE p.score is not null and p.work_id = ").append(workId);
        if (startScore != null && endScore != null) {
            sql.append(" and p.score >= ").append(startScore);
            sql.append(" and p.score <= ").append(endScore);
        }
        if (startScore != null && endScore == null) {
            sql.append(" and p.score = ").append(startScore);
        }
        if (startScore == null && endScore != null) {
            sql.append(" and p.score = ").append(endScore);
        }
        List<Map<String, Object>> paperList = jdbcTemplate.queryForList(sql.toString());
        //图片后缀
        String suffix = ".jpg";
        String dir, localUrl;
        if (Objects.equals("1", imageType)) {
            dir = systemConfig.getSheetDir();
            localUrl = imageServerConfig.getImageServer() + "/sheet";
        } else if (Objects.equals("2", imageType)) {
            dir = systemConfig.getImageDir();
            localUrl = imageServerConfig.getImageServer() + "/images";
        } else {
            throw new RuntimeException("图片类型有误");
        }
        if (paperList != null && paperList.size() > 0) {
            paperList.stream().map(m -> {
                Long studentId = Long.valueOf(m.get("studentId").toString());
                String subject = m.get("subject").toString();
                Subject subject1 = Subject.valueOf(subject);
                String areaCode = m.get("areaCode").toString();
                String examNumber = m.get("examNumber").toString();
                //默认命名为考号
                String fileName = examNumber;
                //图片加密、命名规则为随机码的，都是需要md5
                if (ParamCache.paramMap.get(workId).getNameRule() == 1 || ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                    fileName = MD5Util.getImageRuleMd5(workId, subject1.ordinal(), areaCode, examNumber, studentId);
                }
                String filePath = null;
                if (imageServerConfig.isAliyunOss()) {
                    StringJoiner ossPath = new StringJoiner("/").add(aliYunOssConfig.getUrl()).add(imageServerConfig.getDir()).add(dir.replace("\\", "/")).add(String.valueOf(workId)).add(subject).add(areaCode);
                    filePath = ossPath.toString().concat("/").concat(fileName + suffix);
                } else {
                    String path = localUrl + File.separator + workId + File.separator + subject1 + File.separator + areaCode;
                    filePath = path + File.separator + fileName + suffix;
                }
                //oss或者本地图片访问路径
                m.put("url", filePath);
                return m;
            }).collect(Collectors.toList());

        }
        return paperList;
    }

    //客户端图片导出，单个导出
    @RequestMapping("/file/image/exportScorePicturesFromCollect")
    public void getExportData(@RequestParam Long workId,
                              @RequestParam String imageType,
                              @RequestParam String isWatermark,
                              @RequestParam String nameRule,
                              @RequestParam Long studentId,
                              @RequestParam String studentName,
                              @RequestParam String subject,
                              @RequestParam String areaCode,
                              @RequestParam String examNumber,
                              @RequestParam Double score,
                              @RequestParam Integer idx,
                              HttpServletRequest request,
                              HttpServletResponse response) {
        //图片后缀
        String suffix = ".jpg";

        try {
            if (StringUtils.isBlank(imageType)) {
                throw new RuntimeException("请选择图片类型");
            }
            if (StringUtils.isBlank(nameRule)) {
                throw new RuntimeException("请选择命名规则");
            }

            InputStream inputStream = null;
            OutputStream outputStream = null;
            long start = System.currentTimeMillis();
            try {
                Subject subject1 = Subject.valueOf(subject);

                //默认命名为考号
                String fileName = examNumber;
                //图片加密、命名规则为随机码的，都是需要md5
                if (ParamCache.paramMap.get(workId).getNameRule() == 1 || ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                    fileName = MD5Util.getImageRuleMd5(workId, subject1.ordinal(), areaCode, examNumber, studentId);
                }

                //组装导出目录
                StringJoiner expSj = new StringJoiner(File.separator);
                expSj.add(systemConfig.getLocalhostPath()).add("image-export");
                //原图或者裁切图
                String imageTypeStr = Objects.equals("1", imageType) ? "sheet" : Objects.equals("2", imageType) ? "images" : "default";
                //是否水印
                String waterTypeStr = Objects.equals("1", isWatermark) ? "y" : Objects.equals("0", isWatermark) ? "n" : "default";
                expSj.add(imageTypeStr.concat("-").concat(waterTypeStr));
                LOGGER.info("【{}，{}】开始导出，参数：图片类型：{}，是否有水印：{}，命名规则：{}", examNumber, studentName, imageTypeStr, waterTypeStr, Objects.equals(nameRule, "1") ? "考号+姓名" : "流水号");
                File out = new File(expSj.toString());
                if (!out.exists()) {
                    out.mkdirs();
                }
                //输出命名
                String outFileName;
                if (Objects.equals("1", nameRule)) {
                    StringJoiner sj = new StringJoiner("-");
                    sj.add(examNumber).add(studentName);
                    outFileName = sj.toString();
                } else if (Objects.equals("2", nameRule)) {
                    outFileName = String.valueOf(idx);
                } else {
                    throw new RuntimeException("命名参数有误");
                }

                String dir;
                if (Objects.equals("1", imageType)) {
                    dir = systemConfig.getSheetDir();

                } else if (Objects.equals("2", imageType)) {
                    dir = systemConfig.getImageDir();
                } else {
                    throw new RuntimeException("图片类型有误");
                }

                String outFile = expSj.toString() + File.separator + outFileName + suffix;
                File expFile = null;
                float outputQuality = Objects.equals("1", imageType) ? 0.98f : Objects.equals("2", imageType) ? 0.99f : 1f;
                if (imageServerConfig.isAliyunOss()) {
                    StringJoiner ossSavePath = new StringJoiner("/").add(imageServerConfig.getDir()).add(dir.replace("\\", "/")).add(String.valueOf(workId)).add(subject).add(areaCode);
                    String saveFileName = fileName + suffix;
                    String saveFile = ossSavePath.toString().concat("/").concat(saveFileName);
                    if (Objects.equals("0", isWatermark)) {
                        expFile = ossUtil.ossDownload(saveFile, outFile);
                    }
                    //下载临时原图
                    if (Objects.equals("1", isWatermark)) {
                        long s1 = System.currentTimeMillis();
                        LOGGER.info("【{}，{}】下载临时原图开始", examNumber, studentName);
                        String outSheetFile = expSj.toString() + File.separator + outFileName + "_" + start + suffix;
                        File sheetFileTemp = ossUtil.ossDownload(saveFile, outSheetFile);
                        long s2 = System.currentTimeMillis();
                        LOGGER.info("【{}，{}】下载临时原图结束，总耗时：{}秒", examNumber, studentName, (s2 - s1) / 1000);
                        expFile = new File(outFile);
                        //生成分数水印图片
                        long s3 = System.currentTimeMillis();
                        LOGGER.info("【{}，{}】加水印开始，总耗时：{}秒", examNumber, studentName, (s3 - s2) / 1000);
                        BufferedImage watermarkImage = createWaterImage(score.intValue() + "分");
                        Thumbnails.of(sheetFileTemp).scale(1f).outputQuality(outputQuality).watermark(Positions.BOTTOM_CENTER, watermarkImage, 0.8f).toFile(expFile);
                        long s4 = System.currentTimeMillis();
                        LOGGER.info("【{}，{}】加水印结束，总耗时：{}秒", examNumber, studentName, (s4 - s3) / 1000);
                        sheetFileTemp.delete();
                    }
                } else {
                    String path = dir + File.separator + workId + File.separator + subject1 + File.separator + areaCode;
                    File file = new File(path + File.separator + fileName + suffix);
                    expFile = new File(outFile);
                    if (Objects.equals("0", isWatermark)) {
                        //读取指定路径下面的文件
                        inputStream = new FileInputStream(file);
                        outputStream = new FileOutputStream(expFile);
                        if (ParamCache.paramMap.get(workId).getImageEncrypt() == 1) {
                            outputStream = SystemConstant.writeStream(inputStream, outputStream);
                        } else {
                            outputStream = SystemConstant.writeStreamFomal(inputStream, outputStream);
                        }
                        inputStream.close();
                        outputStream.flush();
                        outputStream.close();
                    }
                    if (Objects.equals("1", isWatermark)) {
                        //生成分数水印图片
                        BufferedImage watermarkImage = createWaterImage(score.intValue() + "分");
                        Thumbnails.of(file).scale(1f).outputQuality(outputQuality).watermark(Positions.BOTTOM_CENTER, watermarkImage, 1f).toFile(expFile);
                    }
                }
                //返回
                exportImage(outFileName + suffix, expFile, response, true);
                long end = System.currentTimeMillis();
                LOGGER.info("【{}，{}】结束导出，总耗时：{}秒", examNumber, studentName, (end - start) / 1000);
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.info("导出失败：{}", e.getMessage());
            } finally {
                try {
                    //强制将缓存区的数据进行输出
                    if (Objects.nonNull(outputStream)) {
                        outputStream.flush();
                        //关流
                        outputStream.close();
                    }
                    if (Objects.nonNull(inputStream)) {
                        inputStream.close();
                    }

                } catch (Exception e) {

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void exportImage(String fileName, File file, HttpServletResponse response, boolean isDeleteLocal) {
        try {
            try {
                if (!file.exists()) {
                    response.sendError(404, "File not found!");
                }

                BufferedInputStream br = new BufferedInputStream(new FileInputStream(file));
                byte[] buf = new byte[1024];
                int len = 0;

                String fName = new String(fileName.getBytes(), "ISO-8859-1");

                response.reset();
                response.setContentType("image/jpeg");
                response.setHeader("Content-Disposition", "attachment; filename=" + fName);

                OutputStream outStream = response.getOutputStream();

                while ((len = br.read(buf)) > 0) {
                    outStream.write(buf, 0, len);
                }

                br.close();
                outStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (isDeleteLocal) {
                    file.delete();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        String msg1 = "90分";
        BufferedImage bi = createWaterImage(msg1);
        // 输出png图片
        ImageIO.write(bi, "png", new File("D:\\company\\qmth\\bat-pro\\msyj-202008\\export\\result.png"));
    }

}
