package com.ruoyi.common.importExcel.utils;

import static com.ruoyi.common.importExcel.excel.ExcelCellValueType.DATE;
import static com.ruoyi.common.importExcel.excel.ExcelCellValueType.DECIMAL;
import static com.ruoyi.common.importExcel.excel.ExcelCellValueType.TIME;
import static java.util.stream.Collectors.toList;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ruoyi.common.exception.ServiceErrorException;
import com.ruoyi.common.importExcel.annotation.ExcelSheetColumn;
import com.ruoyi.common.importExcel.annotation.ExcelSheetDynamicColumn;
import com.ruoyi.common.importExcel.annotation.ExcelSheetInfo;
import com.ruoyi.common.importExcel.excel.ExcelCellValueType;
import com.ruoyi.common.importExcel.excel.helper.BasicExcelHelper;
import com.ruoyi.common.importExcel.excel.helper.BasicTableInfoHelper;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.MessageUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
import lombok.Cleanup;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author qianhuidong
 */
@SuppressWarnings("SpellCheckingInspection")
@Component
public class ExcelUtils {

    public static final String OFFICE_EXCEL_XLS = ".xls";

    public static final String OFFICE_EXCEL_XLSX = ".xlsx";

    public static final String EXCEL_DATE_SEPARATOR = "[-][/]";

    /**
     * @Description 标题信息
     * @Param
     * @Author qianhd
     * @Date 2019/12/14 13:46
     * @Return
     */
    @Data
    public static class HeaderInfo {

        private String title;

        private String dataIndex;

        private String key;

    }

    @Data
    public static class UploadResult<T> {

        // 基本导入信息
        private BasicTableInfoHelper data;

        // 标题行
        private List<HeaderInfo> header;

        // 有效数据
        private List<T> successList;
        // 无效数据
        private List<T> errorList;

        // 表名(临时存储校验结果)
        @JsonIgnore
        private String sheetName;

        // 错误原因(临时存储校验结果)
        private List<String> message;

        // 错误原因(包含多表)getVoucherStatusHistoryList
        private Map<String, List<String>> messages;

        public UploadResult(List<HeaderInfo> header, List<T> successList, List<T> errorList, List<String> message) {
            this.header = header;
            this.successList = successList;
            this.errorList = errorList;
            this.message = message;
            this.messages = Maps.newLinkedHashMap();
        }

        public UploadResult<T> sheet(String sheetName) {
            this.sheetName = sheetName;
            this.messages.put(sheetName, this.message);
            return this;
        }

        public UploadResult<T> join(UploadResult<T> other) {
            this.header.addAll(other.getHeader());
            this.successList.addAll(other.getSuccessList());
            this.errorList.addAll(other.getErrorList());
            this.messages.putAll(other.getMessages());
            return this;
        }
    }

    /**
     * @Description 读取对象中配置的excel标题信息
     * @Param workbook
     * @Author qianhd
     * @Date 2019/12/13 13:43
     * @Return
     */
    public static List<HeaderInfo> readHeader(Class<?> clazz) {
        return Reflections.getDeclaredFields(clazz)
            .stream()
            .filter(field -> {
                ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);
                return mapInfo != null && StringUtils.isNotBlank(mapInfo.colName());
            })
            .map(field -> {
                ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);
                HeaderInfo headerInfo = new HeaderInfo();
                headerInfo.setTitle(mapInfo.colName());

                String propName = field.getName();
                headerInfo.setDataIndex(propName);
                headerInfo.setKey(propName);
                return headerInfo;
            }).collect(toList());
    }

    public static XSSFWorkbook createWorkbook(MultipartFile file) {
        String filename = file.getOriginalFilename();
        //!filename.endsWith(OFFICE_EXCEL_XLS) &&
        if (!filename.endsWith(OFFICE_EXCEL_XLSX)) {
            //            throw new ServiceErrorException("请上传XLSX文件！");
            throw new ServiceErrorException(MessageUtils.message("excel.message.XLSX"));
        }
        XSSFWorkbook workbook = null;
        try {
            @Cleanup
            InputStream inputStream = file.getInputStream();
            workbook = new XSSFWorkbook(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (workbook == null) {
            //            throw new ServiceErrorException("获取工作簿失败");
            throw new ServiceErrorException(MessageUtils.message("excel.message.getSheetFail"));
        }
        return workbook;
    }

    /**
     * @Description 读取工作簿中数据到指定类型的集合中(一行中如果所有数据为空白则去除)
     * @Param file 数据源
     * @Param clazz 返回集合的类型
     * @Author qianhd
     * @Date 2019/12/13 13:43
     * @Return
     */
    public static <T> List<T> readData(XSSFSheet sheet, Class<T> clazz) {
        if (Objects.isNull(sheet)) {
            //            throw new RuntimeException("读取sheet内容失败");
            throw new RuntimeException(MessageUtils.message("excel.message.readSheetFail"));
        }
        // 表格配置信息
        ExcelSheetInfo sheetInfo = clazz.getAnnotation(ExcelSheetInfo.class);

        int headerRowNum = sheetInfo.titleRow();
        int startRowNum = sheetInfo.dataRowStart();
        int step = sheetInfo.step();
        int endColIndex = sheetInfo.endCol();
        String endText = sheetInfo.endColValue();

        List<T> result = new ArrayList<>();
        int lastRowNum = sheet.getLastRowNum();
        if (sheet.getPhysicalNumberOfRows() == 0 && lastRowNum == 0) {
            return result;
        }

        // 1. 检查表头
        Row header = sheet.getRow(headerRowNum);
        if (header == null) {
            //            throw new ServiceErrorException("未找到标题信息，请检查选择的文件是否正确");
            throw new ServiceErrorException(MessageUtils.message("excel.message.noTitle"));
        }
        List<Field> fields = Reflections.getDeclaredFields(clazz);
        for (Field field : fields) {
            ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);
            if (mapInfo == null) {
                continue;
            }
            int colIndex = mapInfo.index();

            Cell cell = header.getCell(colIndex);
            if (cell == null) {
                break;
            }
            String title = getCellValue(cell);
            //String title = getCellValue(getAttrName);
            // 如果为空 可能是合并单元格
            if (StringUtils.isBlank(title)) {
                title = getMergedRegionValue(sheet, headerRowNum, colIndex);
            }

            //if (!title.equals(mapInfo.colName())) {
            if (!title.equals(getColName(mapInfo))) {
                Object[] args = new String[1];
                // args[0] = mapInfo.colName();
                args[0] = getColName(mapInfo);
                throw new ServiceErrorException(MessageUtils.message("excel.message.checkTitle", args));
            }
        }

        // 2. 读取数据, 排除空白行
        for (int rowNum = startRowNum; rowNum <= lastRowNum; rowNum += step) {
            Row row = sheet.getRow(rowNum);

            if (row == null) {
                continue;
            }
            // 如果是结束标志行则返回
            if (StringUtils.isNotBlank(endText)) {
                Cell target = row.getCell(endColIndex);
                String targetText = target != null ? getCellValue(target) : null;
                if (endText.equals(targetText)) {
                    break;
                }
            }

            T t = null;
            try {
                t = clazz.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            // 表信息
            Reflections.invokeSetter(t, "sheetName", sheet.getSheetName());

            boolean hasValue = false;
            // 遍历所有属性，从对应列获取值
            for (Field field : fields) {
                // 1. 普通列处理
                hasValue = hasValue | getFieldData(t, row, field, 0, sheet);

                // 2. 动态扩增列处理
                ExcelSheetDynamicColumn dynamicInfo = field.getAnnotation(ExcelSheetDynamicColumn.class);
                if (dynamicInfo != null) {
                    List<Field> dynamicFields = Reflections.getDeclaredFields(dynamicInfo.clazz(), true);
                    List<Object> childs = Lists.newArrayList();
                    int start = dynamicInfo.startCol();
                    if (dynamicInfo.isSpecial()) {
                        //特殊使用，每一列都是一个实体
                        while (Objects.nonNull(header.getCell(start)) &&
                            StringUtils.isNotBlank(getCellValue(header.getCell(start)))) {
                            Object child = null;
                            try {
                                child = dynamicInfo.clazz().newInstance();
                            } catch (InstantiationException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                            if (Objects.isNull(child)) {
                                throw new ServiceErrorException(MessageUtils.message("excel.message.readFail"));
                            }
                            for (Field dynamic : dynamicFields) {
                                hasValue = hasValue | getFieldDataSpecial(child, row, dynamic, start, sheet,
                                    header.getCell(start).getStringCellValue());
                            }
                            //直接赋值
                            childs.add(child);
                            //固定加一
                            start += 1;
                        }
                    } else {
                        //正常使用 ， 读取实体
                        // 不停读取直到初始列标题为空
                        while (Objects.nonNull(header.getCell(start)) &&
                            StringUtils.isNotBlank(getCellValue(header.getCell(start)))) {
                            Object child = null;
                            try {
                                child = dynamicInfo.clazz().newInstance();
                            } catch (InstantiationException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                            if (Objects.isNull(child)) {
                                throw new ServiceErrorException(MessageUtils.message("excel.message.readFail"));
                            }
                            for (Field dynamic : dynamicFields) {
                                hasValue = hasValue | getFieldData(child, row, dynamic, start, sheet);
                            }

                            // 子项存在除key属性以外其他的值才算存在值
                            for (Field prop : dynamicFields) {
                                String propName = prop.getName();
                                String value = (String)Reflections.invokeGetter(child, propName);
                                if (!dynamicInfo.keyProp().equals(propName) && StringUtils.isNotBlank(value)) {
                                    childs.add(child);
                                    break;
                                }
                            }

                            start += dynamicInfo.cycle();
                        }
                    }
                    Reflections.invokeSetter(t, field.getName(), childs);
                }
            }
            if (hasValue) {
                Reflections.invokeSetter(t, "rowNum", rowNum);
                result.add(t);
            }
        }
        return result;
    }

    /**
     * 写入数据到sheet
     *
     * @param sheet
     * @param dataList
     * @param <T>
     * @return
     */
    public static <T> XSSFSheet writeData(XSSFSheet sheet, List<T> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            return sheet;
        }

        Class clazz = dataList.get(0).getClass();
        ExcelSheetInfo sheetInfo = (ExcelSheetInfo) clazz.getAnnotation(ExcelSheetInfo.class);
        int startRowNum = sheetInfo.dataRowStart();

        List<Field> fields = Reflections.getDeclaredFields(clazz);
        for (T data : dataList) {
            Row row = sheet.createRow(startRowNum);
            for (Field field : fields) {
                setFieldData(data, row, field, 0, sheet);

                ExcelSheetDynamicColumn dynamicColumn = field.getAnnotation(ExcelSheetDynamicColumn.class);
                if (Objects.nonNull(dynamicColumn)) {
                    List<Field> dynamicFields = Reflections.getDeclaredFields(dynamicColumn.clazz());
                    List<Object> childs = (List<Object>) Reflections.invokeGetter(data, field.getName());

                    int start = dynamicColumn.startCol();
                    for (Object child : childs) {
                        if (Objects.nonNull(child)) {
                            for (Field dynamicField : dynamicFields) {
                                setFieldData(child, row, dynamicField, start, sheet);
                            }
                        }

                        start += dynamicColumn.cycle();
                    }
                }
            }
            startRowNum += sheetInfo.step();
        }

        return sheet;
    }

    private static <T> void setFieldData(T data, Row row, Field field, int start, Sheet sheet) {
        ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);
        if (Objects.isNull(mapInfo)) {
            return;
        }
        boolean fixed = mapInfo.row() > -1;
        row = fixed ? sheet.getRow(mapInfo.row()) : row;

        int index = mapInfo.index() + start;
        Cell cell = row.getCell(index);
        if (Objects.isNull(cell)) {
            cell = row.createCell(index);
        }

        Boolean highLighted = (Boolean) Reflections.invokeGetter(data, "highLighted");
        // 设置格式并填充值
        CellStyle cellStyle = fixed ? cell.getCellStyle() : sheet.getColumnStyle(index);
        if (Objects.nonNull(highLighted) && highLighted && !fixed) {
            CellStyle newStyle = sheet.getWorkbook().createCellStyle();
            Font font = sheet.getWorkbook().createFont();
            newStyle.cloneStyleFrom(cellStyle);
            font.setColor(IndexedColors.RED.getIndex());
            newStyle.setFont(font);
            cellStyle = newStyle;
        }
        cell.setCellStyle(cellStyle);

        String cellValue = (String) Reflections.invokeGetter(data, field.getName());
        if (mapInfo.type().equals(DECIMAL) && StringUtils.isNotBlank(cellValue)) {
            cell.setCellValue(new BigDecimal(cellValue).doubleValue());
        } else {
            cell.setCellValue(cellValue);
        }
    }

    private static <T> boolean getFieldData(Object obj, Row row, Field field, int start, Sheet sheet) {
        boolean hasValue = false;
        ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);
        if (mapInfo != null && !mapInfo.ignore()) {
            Cell cell = mapInfo.row() > -1 ?
                sheet.getRow(mapInfo.row()).getCell(start + mapInfo.index())
                : row.getCell(start + mapInfo.index());
            if (cell != null) {
                String cellValue = getCellValue(cell, mapInfo.type());
                if (StringUtils.isNotBlank(cellValue)) {
                    if (mapInfo.row() == -1) {
                        hasValue = true;
                    }
                    Reflections.invokeSetter(obj, field.getName(), cellValue);
                }
            }
        }
        return hasValue;
    }

    /**
     * 特殊保存指定设定
     *
     * @param obj
     * @param row
     * @param field
     * @param start
     * @param sheet
     * @param <T>
     * @return
     */
    private static <T> boolean getFieldDataSpecial(Object obj, Row row, Field field, int start, Sheet sheet,
        String headVal) {
        boolean hasValue = false;
        ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);
        if (mapInfo != null && !mapInfo.ignore()) {
            //获取当前cell值
            Cell cell = mapInfo.row() > -1 ?
                sheet.getRow(mapInfo.row()).getCell(start)
                : row.getCell(start);
            if (cell != null) {
                String cellValue = getCellValue(cell, mapInfo.type());
                if ("label".equals(field.getName())) {
                    cellValue = headVal;
                }
                if (StringUtils.isNotBlank(cellValue)) {
                    if (mapInfo.row() == -1) {
                        hasValue = true;
                    }
                    Reflections.invokeSetter(obj, field.getName(), cellValue);
                }
            }
        }
        return hasValue;
    }

    /**
     * @Description 获取合并单元格的值
     * @Param
     * @Author qianhd
     * @Date 2019/12/23 13:31
     * @Return
     */
    public static String getMergedRegionValue(Sheet sheet, int row, int column) {
        int sheetMergeCount = sheet.getNumMergedRegions();

        for (int i = 0; i < sheetMergeCount; i++) {
            CellRangeAddress ca = sheet.getMergedRegion(i);

            int firstColumn = ca.getFirstColumn();
            int lastColumn = ca.getLastColumn();
            int firstRow = ca.getFirstRow();
            int lastRow = ca.getLastRow();

            if (row >= firstRow && row <= lastRow &&
                column >= firstColumn && column <= lastColumn) {
                return getCellValue(sheet.getRow(firstRow).getCell(firstColumn));
            }
        }

        return "";
    }

    public static String getCellValue(Cell cell) {
        return getCellValue(cell, ExcelCellValueType.STRING);
    }

    /**
     * @Description 获取单元格的值
     * @Param
     * @Author qianhd
     * @Date 2019/12/23 17:02
     * @Return
     */
    public static String getCellValue(Cell cell, ExcelCellValueType columnType) {
        String value = "";
        switch (cell.getCellTypeEnum()) {
            case NUMERIC:
                BigDecimal num = BigDecimal.valueOf(cell.getNumericCellValue());
                if (DateUtil.isCellDateFormatted(cell)) {
                    Date date = DateUtil.getJavaDate(num.doubleValue());
                    if (DATE.equals(columnType)) {
                        value = new SimpleDateFormat("yyyy-MM-dd").format(date);
                    } else if (TIME.equals(columnType)) {
                        value = new SimpleDateFormat("HH:mm").format(date);
                    }

                } else if (new BigDecimal(num.longValue()).compareTo(num) == 0) {
                    value = String.valueOf(num.longValue());
                } else {
                    value = num.toString();
                }
                break;
            case STRING:
                value = getStringCellValue(cell, columnType);
                break;
            case BOOLEAN:
                value = String.valueOf(cell.getBooleanCellValue());
                break;
            case FORMULA:
                // 公式类型
                try {
                    value = String.valueOf(cell.getNumericCellValue());
                } catch (IllegalStateException e) {
                    try {
                        value = String.valueOf(cell.getRichStringCellValue());
                    } catch (IllegalStateException err) {
                        //                        value = "非法字符";
                        value = MessageUtils.message("excel.message.illegalChar");
                    }

                }
                break;
            case BLANK:
                value = "";
                break;
            case ERROR:
                //                value = "非法字符";
                value = MessageUtils.message("excel.message.illegalChar");
                break;
            default:
                //                value = "未知类型";
                value = MessageUtils.message("excel.message.unknowType");
                break;
        }
        return value.trim();
    }

    /**
     * 获取字符串类型单元格的值，包含格式化日期，月份的提取并统一格式化
     *
     * @param cell
     * @param columnType
     * @return
     */
    public static String getStringCellValue(Cell cell, ExcelCellValueType columnType) {
        String value = cell.getStringCellValue().trim();

        DateTimeFormatter monthPattern = DateTimeFormatter.ofPattern("y" + EXCEL_DATE_SEPARATOR + "M");
        DateTimeFormatter datePattern = DateTimeFormatter.ofPattern(
            "y" + EXCEL_DATE_SEPARATOR + "M" + EXCEL_DATE_SEPARATOR + "d");
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        switch (columnType) {
            case MONTH:
                try {
                    value = YearMonth.parse(value, monthPattern)
                        .atDay(1)
                        .format(dateFormatter);
                } catch (DateTimeParseException e) {
                    //                    value = "无效月份";
                    value = MessageUtils.message("excel.message.errorMonthText");

                }
                break;
            case DATE:
                if ("".equals(value)) {
                    return value;
                }
                ;
                try {
                    value = LocalDate.parse(value, datePattern)
                        .format(dateFormatter);
                } catch (DateTimeParseException e) {
                    //                    value = "无效日期";
                    value = MessageUtils.message("excel.message.errorDateText");
                }
                break;
            default:
                break;
        }
        return value;
    }

    public static <T> UploadResult<T> checkData(List<T> list) {
        return checkData(list, t -> Lists.newArrayList());
    }

    /**
     * @Description 从excel文件中获取有效数据和无效数据, 并已指定的对象返回, 去重使用集合的contains方法
     * @Param list 需要校验的集合
     * @Param checkFunc 自定义的判断条件
     * @Author qianhd
     * @Date 2019/12/16 9:49
     * @Return
     */
    public static <T> UploadResult<T> checkData(List<T> list, Function<T, List<String>> businessCheck) {
        List<T> successList = Lists.newArrayList();
        List<T> errorList = Lists.newArrayList();
        List<String> messageList = Lists.newArrayList();

        List<T> valueList = list.stream().filter(Objects::nonNull).collect(toList());
        if (CollectionUtils.isEmpty(valueList)) {
            return new <T>UploadResult(new ArrayList<>(), successList, errorList, messageList);
        }

        Class<?> clazz = valueList.get(0).getClass();
        ExcelSheetInfo sheetInfo = clazz.getAnnotation(ExcelSheetInfo.class);
        int rowNum = 3;
        if (!ObjectUtils.isEmpty(sheetInfo)) {
            rowNum = sheetInfo.dataRowStart() + 1;
        }
        for (T t : list) {
            Object[] args = new Integer[1];
            args[0] = rowNum++;
            if (Objects.nonNull(t)) {
                if (t instanceof BasicExcelHelper) {
                    BasicExcelHelper basicExcelHelper = (BasicExcelHelper)t;
                    args[0] = basicExcelHelper.getRowNum() + 1;
                }
            }

            //            String prefix = "第" + (rowNum++) + "条：";
            String prefix = MessageUtils.message("excel.message.rowNum", args);
            Function<String, String> appendRowNum = msg -> prefix + msg;

            String operation = (String)Reflections.invokeGetter(t, "operation");
            if (t == null || StringUtils.isBlank(operation)) {
                continue;
            }

            // 1. 判断对象所有属性的值是否正确（通用字段格式错误）
            List<String> formatErrors = validateProperties(t, successList).stream().map(appendRowNum).collect(toList());
            boolean hasFormatErrors = CollectionUtils.isNotEmpty(formatErrors);
            if (hasFormatErrors) {
                messageList.addAll(formatErrors);
            }

            // 2. 自定义校验(业务错误)
            List<String> businessErrors = businessCheck.apply(t).stream().map(appendRowNum).collect(toList());
            boolean hasBusinessErrors = CollectionUtils.isNotEmpty(businessErrors);
            if (hasBusinessErrors) {
                messageList.addAll(businessErrors);
            }

            if (!hasFormatErrors && !hasBusinessErrors) {
                successList.add(t);
            } else {
                errorList.add(t);
            }
        }

        return new <T>UploadResult(readHeader(list.get(0).getClass()), successList, errorList, messageList);
    }

    /**
     * @return
     * @Description 检查对象属性是否满足条件(根据字段类型设置默认的校验方法)
     * @Param
     * @Author qianhd
     * @Date 2019/12/13 17:41
     * @Return
     */
    public static <T> Collection<String> validateProperties(T t, List<T> successList) {
        List<String> result = new ArrayList<>();

        validateCommonProperties(t, successList, result, null);

        return result;
    }

    private static <T> void validateCommonProperties(T t, List<T> successList, List<String> result, String itemName) {
        Map<String, List<String>> uniqueGroups = Maps.newHashMap();
        Long customerId = 0L;
        for (Field field : Reflections.getDeclaredFields(t.getClass())) {
            ExcelSheetColumn mapInfo = field.getAnnotation(ExcelSheetColumn.class);

            if (Objects.nonNull(mapInfo)) {
                //String cName2 = mapInfo.colName();
                String cName = getColName(mapInfo);
                String colName = StringUtils.isNotBlank(itemName) ? "[" + itemName + "]" + cName : cName;
                String fieldName = field.getName();
                String propValue = (String)Reflections.invokeGetter(t, fieldName);

                // 判断为空的情况
                if (StringUtils.isBlank(propValue)) {
                    if (mapInfo.notBlank()) {
                        //                        result.add(colName + "不能为空");
                        result.add(colName + MessageUtils.message("excel.message.notEmpty"));
                    }
                    continue;
                }

                // 判断重复
                if (mapInfo.unique()) {
                    String groupName = mapInfo.uniqueGroup();
                    // 无分组情况
                    if (StringUtils.isBlank(groupName)) {
                        for (T exist : successList) {
                            String existValue = (String)Reflections.invokeGetter(exist, fieldName);
                            if (propValue.equals(existValue)) {
                                //                                result.add(colName + "不能重复");
                                result.add(colName + MessageUtils.message("excel.message.notSame"));
                                break;
                            }
                        }
                    } else {
                        List<String> groupFields = uniqueGroups.get(groupName);
                        if (CollectionUtils.isEmpty(groupFields)) {
                            uniqueGroups.put(groupName, Lists.newArrayList(fieldName));
                        } else {
                            groupFields.add(fieldName);
                        }
                    }
                }

                // 非空时校验-------------
                int maxLength = mapInfo.maxLength();
                if (maxLength > 0 && propValue.length() > maxLength) {
                    //                    result.add(colName + "长度不能大于" + maxLength);
                    result.add(colName + MessageUtils.message("excel.message.maxLength") + maxLength);
                    continue;
                }
                int minLength = mapInfo.minLength();
                if (minLength > 0 && propValue.length() < minLength) {
                    //                    result.add(colName + "长度不能小于" + maxLength);
                    result.add(colName + MessageUtils.message("excel.message.minLength") + minLength);
                    continue;
                }

                // 通用类型的校验，校验需要保存的值
                switch (mapInfo.type()) {
                    case DECIMAL:
                        BigDecimal value;
                        String msg;
                        try {
                            value = new BigDecimal(propValue);
                            msg = checkBigdeciLengthMsg(colName, value, mapInfo.intNum(), mapInfo.precision(), true);
                        } catch (NumberFormatException e) {
                            //                            msg = colName + "只能输入数字";
                            msg = colName + MessageUtils.message("excel.message.nolyNumber");
                        }
                        if (StringUtils.isNotBlank(msg)) {
                            result.add(msg);
                        }
                        break;
                    case NUMBER:
                        if (!IsNumber(propValue)) {

                            //                            result.add(colName + "只能输入数字");
                            result.add(colName + MessageUtils.message("excel.message.nolyNumber"));
                        }
                        break;
                    case MOBILE:
                        if (!IsMobile(propValue)) {
                            //                            result.add(colName + "手机号必须为11位数字");
                            result.add(colName + MessageUtils.message("excel.message.phone"));
                        }
                        break;
                    case EMAIL:
                        if (propValue.indexOf("@") == -1) {
                            result.add(colName + MessageUtils.message("excel.message.errorFormat"));
                            //                            result.add(colName + "格式错误");
                        }
                        break;
                    case MONTH:
                        if (!isDate(propValue)) {
                            result.add(colName + MessageUtils.message("excel.message.errorMonth"));
                            //                            result.add(colName + "无效的月份格式（2024-09或者2024/09）");
                        }
                        break;
                    case DATE:
                        if (!isDate(propValue)) {
                            //                            result.add(colName + "无效的日期格式（2024-09-11或者2024/09/11）");
                            result.add(colName + MessageUtils.message("excel.message.errorDate"));
                        }
                        break;
                    case TIME:
                        if (!isTime(propValue)) {
                            //                            result.add(colName + "无效的时间格式（17:33）");
                            result.add(colName + MessageUtils.message("excel.message.errorTime"));
                        }
                        break;
                    case DICT:
                        String dictvalue = DictUtils.getDictValue(mapInfo.dictKey(), propValue);
                        if (StringUtils.isBlank(dictvalue)) {
                            //                            result.add(colName + "不存在");
                            result.add(colName + MessageUtils.message("excel.message.undefine"));
                        } else {
                            Reflections.invokeSetter(t, fieldName, dictvalue);
                        }
                    default:
                        break;
                }
                continue;
            }

            ExcelSheetDynamicColumn dynamicColumn = field.getAnnotation(ExcelSheetDynamicColumn.class);
            if (Objects.nonNull(dynamicColumn)) {
                List<Object> childs = (List<Object>)Reflections.invokeGetter(t, field.getName());

                List<Object> successChilds = Lists.newArrayList();
                for (Object child : childs) {
                    if (Objects.nonNull(child)) {
                        String keyProp = dynamicColumn.keyProp();
                        String keyPropValue = StringUtils.isNotBlank(keyProp) ? (String)Reflections.invokeGetter(child,
                            dynamicColumn.keyProp()) : null;
                        validateCommonProperties(child, successChilds, result, keyPropValue);
                        successChilds.add(child);
                    }
                }
            }
        }

        // 分组校验
        successList.forEach(exist -> {
            for (String groupName : uniqueGroups.keySet()) {
                boolean same = true;
                for (String fieldName : uniqueGroups.get(groupName)) {
                    String fieldValue = (String)Reflections.invokeGetter(t, fieldName);
                    String existValue = (String)Reflections.invokeGetter(exist, fieldName);
                    if (!fieldValue.equals(existValue)) {
                        same = false;
                        break;
                    }
                }
                if (same) {
                    result.add(groupName);
                    break;
                }
            }
        });
    }

    /**
     * 判断Bigdecimal精度 value:字符串 checkLength:长度 mustFlag是否必须 返回值 "备注不能为空   备注超过256位"
     */
    public static String checkBigdeciLengthMsg(String msgName, BigDecimal value, Integer intnum, Integer decinum,
        boolean mustFlag) {
        String checkMsg = "";

        if (mustFlag && value == null) {
            //            checkMsg = msgName + "不能为空；";
            checkMsg = msgName + MessageUtils.message("excel.message.notEmpty");
        } else if (!mustFlag && value == null) {
        } else {
            if (value.compareTo(BigDecimal.ZERO) < 0) {
                //                checkMsg = msgName + "不能小于0";
                checkMsg = msgName + MessageUtils.message("excel.message.zero");
            } else {
                String result = "";
                String intStr = String.valueOf(value.intValue());
                result += msgName;
                if (intStr.length() > intnum) {
                    //                    result += "整数位不能大于" + intnum + "位";
                    Object[] args = new Integer[1];
                    args[0] = intnum;
                    result += MessageUtils.message("excel.message.int", args);
                }

                int deciStr = value.scale();
                if (deciStr > decinum) {
                    if (StringUtils.isNotBlank(result)) {
                        result += "，";
                    }
                    //                    result += "小数位不能大于" + decinum + "位";
                    Object[] args = new Integer[1];
                    args[0] = decinum;
                    result += MessageUtils.message("excel.message.double", args);
                }

                if (StringUtils.equals(result, msgName)) {
                    result = "";
                }
                checkMsg = result;
            }
        }
        return checkMsg;
    }

    /**
     * 验证电话号码
     *
     * @param
     * @return 如果是符合格式的字符串, 返回 <b>true </b>,否则为 <b>false </b>
     */
    public static boolean IsMobile(String str) {
        String regex = "^[0-9]{11}$";
        return Pattern.matches(regex, str);
    }

    public static boolean IsNumber(String str) {
        String regex = "^[0-9]*$";
        return Pattern.matches(regex, str);
    }

    public static boolean isDate(String value) {
        String[] parsePatterns = new String[]{"yyyy-MM-dd", "yyyy/MM/dd"};
        try {
            DateUtils.parseDate(value, parsePatterns);
        } catch (ParseException e) {
            return false;
        }
        return true;
    }

    public static boolean isTime(String value) {
        try {
            LocalTime localTime = LocalTime.parse(value, DateTimeFormatter.ofPattern("HH:mm"));
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }


    private static String getColName(ExcelSheetColumn column) {
        if (StringUtils.isNotBlank(column.i18nCode())) {
            return MessageUtils.message(column.i18nCode());
        }
        return column.colName();
    }
}
