package com.dayou.utils;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dayou.annotation.ExcelSheet;
import com.dayou.annotation.ExportCell;
import com.dayou.annotation.ImportCell;
import com.dayou.common.Null;
import com.dayou.enums.BatchTaskTypeEnum;
import com.dayou.enums.CodeMsgEnumInterface;
import com.dayou.exception.BusinessException;
import com.dayou.exception.ErrorCode;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
import org.apache.poi.xssf.usermodel.XSSFName;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @Auther: miracle
 * @Date: 2019/1/16 0016
 */
public class ExcelUtil {
    private static final Logger logger = LoggerFactory.getLogger(ExcelUtil.class);
    public static final String TMPL_DIVIDER = "N";

    private static final DateTimeFormatter YMD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter YMD2 = DateTimeFormatter.ofPattern("yyyy.MM.dd");
    private static final DateTimeFormatter YMDHMS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static final int IMPORT_MAX_ROW = 10000;

    public static <T> void createTitle(XSSFWorkbook wb, Class<T> clazz) throws IllegalAccessException,
            InstantiationException {
        ExcelSheet excelSheet = clazz.getAnnotation(ExcelSheet.class);
        if (excelSheet == null) {
            throw new IllegalArgumentException("类没有ExcelSheet注解");
        }
        Sheet sheet = wb.createSheet(excelSheet.sheetName());
        Row row = sheet.createRow(0);
        Cell cell = null;
        Field[] fields = clazz.getDeclaredFields();
        int column = 0;
        for (Field field : fields) {
            field.setAccessible(true);
            ExportCell exportCell = field.getAnnotation(ExportCell.class);
            if (exportCell != null) {
                cell = row.createCell(column);
                cell.setCellValue(exportCell.columnName());
                column++;
            }
        }

    }

    public static <T> void appendList2SheetByAnnotation(Sheet sheet, List<T> list) throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        Workbook wb = sheet.getWorkbook();
        Row row = null;
        int lastRow = sheet.getLastRowNum();
        for (int i = 1; i <= list.size(); i++) {
            T vo = list.get(i - 1);
            row = sheet.createRow(lastRow + i);
            Field[] fields = vo.getClass().getDeclaredFields();
            Method method = null;
            Field field = null;
            Cell cell = null;
            int column = 0;
            for (int j = 0; j < fields.length; j++) {
                field = fields[j];
                ExportCell exportCell = field.getAnnotation(ExportCell.class);
                if (exportCell != null) {
                    cell = row.createCell(column);
                    String name = field.getName();
                    String upperName = name.substring(0, 1).toUpperCase() + name.substring(1);
                    method = vo.getClass().getMethod("get" + upperName);
                    if (method == null) {
                        continue;
                    }
                    Object temp = method.invoke(vo);
                    if (temp == null) {
                        column++;
                        continue;
                    }
                    String fieldType = field.getType().getSimpleName();
                    if ("String".equals(fieldType)) {
                        cell.setCellValue((String) method.invoke(vo));
                    } else if ("Integer".equals(fieldType) || "int".equals(fieldType)) {
                        Integer value = (Integer) method.invoke(vo);
                        cell.setCellValue(value);
                    } else if ("Boolean".equals(fieldType) || "boolean".equals(fieldType)) {
                        Boolean value = (Boolean) method.invoke(vo);
                        cell.setCellValue(value);
                    } else if ("Double".equals(fieldType) || "double".equals(fieldType)) {
                        Double value = (Double) method.invoke(vo);
                        cell.setCellValue(value);
                    } else if ("BigDecimal".equals(fieldType)) {
                        BigDecimal value = (BigDecimal) method.invoke(vo);
                        cell.setCellValue(value.doubleValue());
                    } else if ("Long".equals(fieldType) || "long".equals(fieldType)) {
                        Long value = (Long) method.invoke(vo);
                        cell.setCellValue(value);
                    } else if ("Date".equals(fieldType)) {
                        CellStyle cellStyle = wb.createCellStyle();
                        DataFormat dataFormat = wb.createDataFormat();
                        cellStyle.setDataFormat(dataFormat.getFormat(DateUtils.YYYY_MM_DD_HH_MM_SS));
                        cell.setCellStyle(cellStyle);
                        cell.setCellValue((Date) method.invoke(vo));
                    } else if ("LocalDate".equals(fieldType)) {
                        cell.setCellValue(YMD.format((LocalDate) method.invoke(vo)));
                    } else if ("LocalDateTime".equals(fieldType)) {
                        cell.setCellValue(YMDHMS.format((LocalDateTime) method.invoke(vo)));
                    }

                    Class clazz = exportCell.ref();
                    if (!clazz.equals(Null.class)) {
                        if (clazz.isEnum()) {
                            Object[] objects = clazz.getEnumConstants();
                            for (Object o : objects) {
                                CodeMsgEnumInterface codeMsgEnumInterface = (CodeMsgEnumInterface) o;
                                Object value = method.invoke(vo);
                                if (value == null) {
                                    continue;
                                }
                                if (value.equals(codeMsgEnumInterface.getCode())) {
                                    cell.setCellValue((String) codeMsgEnumInterface.getMsg());
                                }
                            }
                        }
                    }
                    column++;
                }
            }
        }
    }

    public static <T> T convertRow2Model(Row row, Class<T> clazz) throws IllegalAccessException,
            InstantiationException {
        int rowNum = row.getRowNum();
        Field[] fields = clazz.getDeclaredFields();
        T t = clazz.newInstance();

//        for (int i = 0; i < fields.length; i++) {
//            Cell cell = row.getCell(i);
//            if (getCellValue(cell) != null) {
//                break;
//            }
//            if (i == fields.length - 1) {
//                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "存在空行，请检查表格尾部是否有曾经存在数据的空白行，请删除这些空白行");
//            }
//        }

        int index = 0;
        for (Field field : fields) {
            field.setAccessible(true);
            ImportCell annotation = field.getAnnotation(ImportCell.class);
            if (annotation != null) {
                boolean notNull = annotation.notNull();
                Cell cell = row.getCell(index);


                if (getCellValue(cell) == null && !notNull) {
                    index++;
                    continue;
                }

                if (getCellValue(cell) == null && notNull) {
                    //ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列:不能为空");
                    return null;
                }

                if (CellType.ERROR.equals(cell.getCellType())) {
                    index++;
                    continue;
                }

                //处理空cell情况
                if (notNull && getCellValue(cell) == null) {
                    //ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列不能为空");
                    return null;
                }

                if(field.getType().equals(String.class) && !cell.getCellType().equals(CellType.STRING)){
                    cell.setCellType(CellType.STRING);
                }

                Object value = null;
                try {
                    // 处理特殊情况,Excel中的String,转换成Bean的Date
                    if (field.getType().equals(Date.class) && cell.getCellType() == CellType.STRING) {
                        try {
                            Object strDate = getCellValue(cell);
                            value = DateUtils.parseDate(strDate);
                        } catch (Exception e) {
                            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列日期转换失败");
                        }
                    } else if (field.getType().equals(LocalDate.class) && cell.getCellType() == CellType.STRING) {
                        try {
                            value = DateUtils.parseLocalDate(String.valueOf(getCellValue(cell)));
                        } catch (Exception e) {
                            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列日期转换失败.");
                        }
                    } else if (field.getType().equals(String.class) && cell.getCellType() == CellType.NUMERIC) {
                        // 兼容cell double列String情况  经测试加上这句有精度丢失bug
                        // cell.setCellType(CellType.STRING);
                        Object cellValue = getCellValue(cell);
                        value = Objects.isNull(cellValue) ? null : String.valueOf(cellValue);
                    } else if (field.getType().equals(LocalDate.class) && cell.getCellType() == CellType.NUMERIC) {
                        value = ((Date) getCellValue(cell)).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                    } else if (field.getType().equals(BigDecimal.class) && cell.getCellType() == CellType.NUMERIC) {
                        // 兼容cell double列BigDecimal情况
                        value = BigDecimal.valueOf((Double) getCellValue(cell));
                    } else if (field.getType().equals(Integer.class) && cell.getCellType() == CellType.NUMERIC) {
                        value = Integer.valueOf(((Double) getCellValue(cell)).intValue());
                    } else if (field.getType().equals(Long.class) && cell.getCellType() == CellType.NUMERIC) {
                        value = NumberUtil.parseLong(String.valueOf(getCellValue(cell)));
                    } else if (field.getType().equals(Boolean.class) && cell.getCellType() == CellType.NUMERIC) {
                        value = NumberUtil.parseInt(String.valueOf(getCellValue(cell))) == 1 ? true : false;
                    } else {
                        value = getCellValue(cell);
                        // fix bug20201127
                        if ((Integer.TYPE == field.getType()) || (Integer.class == field.getType())) {
                            value = Integer.valueOf(String.valueOf(value));
                        } else if ((Long.TYPE == field.getType()) || (Long.class == field.getType())) {
                            value = Long.valueOf(String.valueOf(value));
                        } else if ((Double.TYPE == field.getType()) || (Double.class == field.getType())) {
                            value = Double.valueOf(String.valueOf(value));
                        } else if ((Float.TYPE == field.getType()) || (Float.class == field.getType())) {
                            value = Float.valueOf(String.valueOf(value));
                        } else if (BigDecimal.class == field.getType()) {
                            value = new BigDecimal(String.valueOf(value));
                        }
                    }
                } catch (Exception e) {
                    if (e instanceof BusinessException) {
                        throw e;
                    }
                    logger.error("parse error.", e);
                    ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列格式异常");
                }


                //处理枚举情况，将输入的字符串转换为枚举对应的code
                Class refClazz = annotation.ref();
                if (!refClazz.isInstance(Null.class) && value != null) {
                    if (refClazz.isEnum()) {
                        CodeMsgEnumInterface codeMsgEnumInterface = EnumUtils.match(value, refClazz, null);
                        if (codeMsgEnumInterface == null) {
                            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列:值不在可选范围内");
                        }
                        value = codeMsgEnumInterface != null ? codeMsgEnumInterface.getCode() : null;
                    }
                }
                try {
                    field.set(t, value);
                } catch (IllegalArgumentException e) {
                    logger.error("field set error.", e);
                    ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (index + 1) + "列:字段类型异常");
                }
                index++;
            }
        }
        return t;
    }

    /**
     * 获取单元格值
     *
     * @param cell
     * @return
     */
    public static Object getCellValue(Cell cell) {
        if (cell == null
                || (cell.getCellType() == CellType.STRING && StringUtils.isBlank(cell.getStringCellValue()))) {
            return null;
        }
        CellType cellType = cell.getCellType();
        if (cellType == CellType.BLANK) {
            return null;
        } else if (cellType == CellType.BOOLEAN) {
            return cell.getBooleanCellValue();
        } else if (cellType == CellType.ERROR) {
            return cell.getErrorCellValue();
        } else if (cellType == CellType.FORMULA) {
            try {
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue();
                } else {
                    return cell.getNumericCellValue();
                }
            } catch (IllegalStateException e) {
                return cell.getRichStringCellValue();
            }
        } else if (cellType == CellType.NUMERIC) {
            if (DateUtil.isCellDateFormatted(cell)) {
                return cell.getDateCellValue();
            } else {
                return cell.getNumericCellValue();
            }
        } else if (cellType == CellType.STRING) {
            return cell.getStringCellValue();
        } else {
            return null;
        }
    }

    /**
     * 通用导出工具
     * 默认单页大小1000
     *
     * @param clazz
     * @param exportListener
     * @param req
     * @param <T>
     * @param <Req>
     * @return
     */
    public static <T, Req> XSSFWorkbook export(Class<T> clazz, ExportListener<T, Req> exportListener, Req req) {
        return export(clazz, exportListener, req, 1000);
    }

    /**
     * 通用导出工具
     *
     * @param clazz
     * @param exportListener
     * @param req
     * @param batch
     * @param <T>
     * @param <Req>
     * @return
     */
    public static <T, Req> XSSFWorkbook export(Class<T> clazz, ExportListener<T, Req> exportListener, Req req,
                                               int batch) {
        XSSFWorkbook wb = new XSSFWorkbook();
        int current = 1, size = batch;
        Page page = new Page(current, size);
        try {
            ExcelUtil.createTitle(wb, clazz);
            IPage<T> pages = exportListener.queryPages(page, req);
            List<T> list = pages.getRecords();
            Sheet sheet = wb.getSheetAt(0);
            while (CollectionUtils.isNotEmpty(list)) {
                ExcelUtil.appendList2SheetByAnnotation(sheet, list);
                current++;
                page = new Page(current, size);
                pages = exportListener.queryPages(page, req);
                list = pages.getRecords();
            }
        } catch (Exception e) {
            logger.error("导出列表异常", e);
            ErrorCode.throwBusinessException(ErrorCode.DEFAULTERROR);
        }

        return wb;
    }

    public static <T> void importExcel(Class<T> clazz, ImportListener<T> importListener, MultipartFile file,
                                       BatchTaskTypeEnum batchTaskTypeEnum, BatchImportStrategy batchImportStrategy) {
        importExcel(clazz, importListener, file, batchTaskTypeEnum, batchImportStrategy, 1);
    }

    public static <T> void importExcel(Class<T> clazz, ImportListener<T> importListener, MultipartFile file,
                                       BatchTaskTypeEnum batchTaskTypeEnum, BatchImportStrategy batchImportStrategy, int startRow) {

        BatchImportResult batchImportResult = new BatchImportResult();
        int success = 0, fail = 0;
        List<BatchImportResult.FailResult> failResults = new LinkedList<>();
        try {
            InputStream ins = file.getInputStream();
            Workbook wb = WorkbookFactory.create(ins);
            Sheet sheet = wb.getSheetAt(0);
            int maxRowNum = sheet.getLastRowNum();
            if (maxRowNum >= IMPORT_MAX_ROW) {
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "导入数量不能超过" + IMPORT_MAX_ROW + "行");
            }

            if (maxRowNum < startRow) {
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "无数据");
            }

            for (int i = startRow; i <= maxRowNum; i++) {
                Row row = sheet.getRow(i);
                try {
                    if (row != null) {
                        long start = System.currentTimeMillis();
                        T t = ExcelUtil.convertRow2Model(row, clazz);
                        Boolean ret = importListener.handleOneRecord(t);
                        if (ret) {
                            success++;
                            long end = System.currentTimeMillis();
                            logger.info(MessageFormat.format("成功导入第{0}行,cost:{1}ms...", i, (end - start)));
                            continue;
                        }
                    }
                    fail++;
                } catch (Throwable e) {
                    fail++;
                    String reason = getReasonByException(e);
                    BatchImportResult.FailResult failResult = new BatchImportResult.FailResult(i, reason);
                    failResults.add(failResult);
                    if (BatchImportStrategy.ROLL_BACK.equals(batchImportStrategy)) {
                        if (e instanceof BusinessException || e instanceof DuplicateKeyException || e instanceof DataIntegrityViolationException) {
                            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (i + 1) + "行:" + reason);
                        }
                        throw e;
                    }
                    if (e instanceof BusinessException) {
                        logger.error(MessageFormat.format("批量导入失败,row:{0}", i));
                    } else {
                        logger.error(MessageFormat.format("批量导入失败,row:{0}", i), e);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("", e);
            if (e instanceof BusinessException) {
                throw (BusinessException) e;
            }
            ErrorCode.throwBusinessException(ErrorCode.DEFAULTERROR);

        } finally {
            batchImportResult.setSuccess(success);
            batchImportResult.setFail(fail);
            batchImportResult.setBatchTaskTypeEnum(batchTaskTypeEnum);
            if (CollectionUtils.isNotEmpty(failResults)) {
                batchImportResult.setDetail(JSON.toJSONString(failResults));
            }
            if (BatchImportStrategy.RECORD_ERROR.equals(batchImportStrategy)) {
                importListener.afterImport(batchImportResult);
            }

        }

    }

    public static <T> List<T> importExcel(Class<T> clazz,ImportListener<T> importListener, MultipartFile file, int startRow) {
        List<T> result = new LinkedList<>();
        List<BatchImportResult.FailResult> failResults = new LinkedList<>();
        try {
            InputStream ins = file.getInputStream();
            Workbook wb = WorkbookFactory.create(ins);
            Sheet sheet = wb.getSheetAt(0);
            for (Iterator ite = sheet.rowIterator(); ite.hasNext(); ) {
                Row row = (Row) ite.next();
                for (Iterator itet = row.cellIterator(); itet.hasNext(); ) {
                    Cell cell = (Cell) itet.next();
                    cell.setCellType(CellType.STRING);
                }
            }
            int maxRowNum = sheet.getLastRowNum();
            if (maxRowNum >= IMPORT_MAX_ROW) {
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "导入数量不能超过" + IMPORT_MAX_ROW + "行");
            }

            if (maxRowNum < startRow) {
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "无数据");
            }

            for (int i = startRow; i <= maxRowNum; i++) {
                Row row = sheet.getRow(i);
                try {
                    if (row != null) {
                        T t = ExcelUtil.convertRow2Model(row, clazz);
                        if (t==null){
                            continue;
                        }
                        Boolean ret = importListener.handleOneRecord(t);
                        if (ret){
                            result.add(t);
                        }
                    }
                } catch (Throwable e) {
                    String reason = getReasonByException(e);
                    BatchImportResult.FailResult failResult = new BatchImportResult.FailResult(i, reason);
                    failResults.add(failResult);
                    if (e instanceof BusinessException) {
                        logger.error(MessageFormat.format("批量导入失败,row:{0}", i));
                        ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (i + 1) + "行:" + reason);
                    } else {
                        logger.error(MessageFormat.format("批量导入失败,row:{0}", i), e);
                        throw e;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("", e);
            if (e instanceof BusinessException) {
                throw (BusinessException) e;
            }
            ErrorCode.throwBusinessException(ErrorCode.DEFAULTERROR);
        }

        return result;
    }

    public static <T> List<T> importExcel(Class<T> clazz, InputStream in, int startRow) {
        List<T> result = new LinkedList<>();
        List<BatchImportResult.FailResult> failResults = new LinkedList<>();
        try {
            InputStream ins = in;
            Workbook wb = WorkbookFactory.create(ins);
            Sheet sheet = wb.getSheetAt(0);
            for (Iterator ite = sheet.rowIterator(); ite.hasNext(); ) {
                Row row = (Row) ite.next();
                for (Iterator itet = row.cellIterator(); itet.hasNext(); ) {
                    Cell cell = (Cell) itet.next();
                    cell.setCellType(CellType.STRING);
                }
            }
            int maxRowNum = sheet.getLastRowNum();
            if (maxRowNum >= IMPORT_MAX_ROW) {
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "导入数量不能超过" + IMPORT_MAX_ROW + "行");
            }

            if (maxRowNum < startRow) {
                ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "无数据");
            }

            for (int i = startRow; i <= maxRowNum; i++) {
                Row row = sheet.getRow(i);
                try {
                    if (row != null) {
                        result.add(ExcelUtil.convertRow2Model(row, clazz));
                    }
                } catch (Throwable e) {
                    String reason = getReasonByException(e);
                    BatchImportResult.FailResult failResult = new BatchImportResult.FailResult(i, reason);
                    failResults.add(failResult);
                    if (e instanceof BusinessException) {
                        logger.error(MessageFormat.format("批量导入失败,row:{0}", i));
                        ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR, "第" + (i + 1) + "行:" + reason);
                    } else {
                        logger.error(MessageFormat.format("批量导入失败,row:{0}", i), e);
                        throw e;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("", e);
            if (e instanceof BusinessException) {
                throw (BusinessException) e;
            }
            ErrorCode.throwBusinessException(ErrorCode.DEFAULTERROR);
        }

        return result;
    }


    private static String getReasonByException(Throwable e) {
        String msg = e.getMessage();
        String reason = null;
        if (e instanceof BusinessException) {
            reason = msg;
        } else if (e instanceof DuplicateKeyException) {
            if (msg.contains("Duplicate entry")) {
                String regx = "Duplicate entry '(.*)' for key";
                reason = MessageFormat.format("[{0}]重复", RegxUtil.findFirstMatchStr(msg, regx));
            }
        } else if (e instanceof DataIntegrityViolationException) {
            if (msg.contains("Data too long for column")) {
                String regx = "Data too long for column '(.*)'";
                reason = MessageFormat.format("[{0}]数据太长了", RegxUtil.findFirstMatchStr(msg, regx));
            }
        } else {
            reason = ErrorCode.DEFAULTERROR.getErrorMsg();
        }

        return reason;
    }

    public static enum BatchImportStrategy {
        /**
         * 出错直接抛出异常回滚
         */
        ROLL_BACK,
        /**
         * 出错catch后记录异常，并继续执行
         */
        RECORD_ERROR;
    }

    public static class BatchImportResult {
        private Integer success;
        private Integer fail;
        private String detail;
        private BatchTaskTypeEnum batchTaskTypeEnum;

        public BatchImportResult() {
        }

        public BatchImportResult(Integer success, Integer fail, String detail) {
            this.success = success;
            this.fail = fail;
            this.detail = detail;
        }


        public static class FailResult implements Serializable {
            private Integer row;
            private String reason;

            public FailResult(Integer row, String reason) {
                this.row = row;
                this.reason = reason;
            }

            public Integer getRow() {
                return row;
            }

            public void setRow(Integer row) {
                this.row = row;
            }

            public String getReason() {
                return reason;
            }

            public void setReason(String reason) {
                this.reason = reason;
            }
        }

        public BatchTaskTypeEnum getBatchTaskTypeEnum() {
            return batchTaskTypeEnum;
        }

        public void setBatchTaskTypeEnum(BatchTaskTypeEnum batchTaskTypeEnum) {
            this.batchTaskTypeEnum = batchTaskTypeEnum;
        }

        public Integer getSuccess() {
            return success;
        }

        public void setSuccess(Integer success) {
            this.success = success;
        }

        public Integer getFail() {
            return fail;
        }

        public void setFail(Integer fail) {
            this.fail = fail;
        }

        public String getDetail() {
            return detail;
        }

        public void setDetail(String detail) {
            this.detail = detail;
        }
    }

    public interface ImportListener<T> {
        Boolean handleOneRecord(T t);

        default void afterImport(BatchImportResult batchImportResult) {
        }

        ;
    }

    public interface ExportListener<T, Req> {
        IPage<T> queryPages(Page page, Req req);
    }

    /**
     * 创建名称
     *
     * @param wb
     * @param name
     * @param expression
     * @return
     */
    public static XSSFName createName(XSSFWorkbook wb, String name, String expression) {
        try {
            XSSFName refer = wb.createName();
            refer.setRefersToFormula(expression);
            refer.setNameName(name);
            return refer;
        } catch (Exception e) {
            logger.error("excel创建表达式异常", e);
            throw e;
        }
    }

    /**
     * 添加数据验证选项
     *
     * @param
     */
    public static void setDataValidation(XSSFSheet sheet, String name, int startRow, int endRow, int startCol,
                                         int endCol, boolean isTip) {
        try {
            XSSFDataValidationHelper helper = new XSSFDataValidationHelper(sheet);
            DataValidationConstraint constraint = helper.createFormulaListConstraint(name);
            CellRangeAddressList regions = new CellRangeAddressList((short) startRow, (short) endRow,
                    (short) startCol, (short) endCol);
            // 数据有效性对象
            DataValidation dataValidation = helper.createValidation(constraint, regions);
            if (isTip) {
                //设置输入信息提示信息
//                dataValidation.createPromptBox("下拉选择提示", "请使用下拉方式选择合适的值！");
                //设置输入错误提示信息
                dataValidation.createErrorBox("选择错误提示", "你输入的值未在备选列表中，请下拉选择合适的值！");

                dataValidation.setShowErrorBox(true);
//                dataValidation.setShowPromptBox(true);
            }
            sheet.addValidationData(dataValidation);
        } catch (Exception e) {
            logger.error("设置数据有效性校验异常", e);
            throw e;
        }
    }
}
