package jnpf.util.excelv2.util;


import cn.hutool.core.util.StrUtil;
import jnpf.util.excelv2.adapter.SelfDataConversionAdapter;
import jnpf.util.excelv2.annotation.Excel;
import jnpf.util.excelv2.annotation.Excels;
import jnpf.util.excelv2.domain.ExcelBeanMethodParseVo;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

public class ExcelUtils<T> {
    private static final Logger log = LoggerFactory.getLogger(ExcelUtils.class);

    public static final String SEPARATOR = ",";

    public static final String FORMULA_REGEX_STR = "=|-|\\+|@";

    public static final String[] FORMULA_STR = {"=", "-", "+", "@"};


    /**
     * Spring 应用上下文环境
     */
    private static volatile ConfigurableListableBeanFactory beanFactory;

    /**
     * Excel sheet最大行数，默认65536
     */
    public static final int sheetSize = 65536;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 导出类型（EXPORT:导出数据；IMPORT：导入模板）
     */
    private Excel.Type type;

    /**
     * 工作薄对象
     */
    private Workbook wb;

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

    /**
     * 导入导出数据列表
     */
    private List<T> list;

    /**
     * 注解列表
     */
    private List<Object[]> fields;

    /**
     * 当前行号
     */
    private int rowNum;

    /**
     * 标题
     */
    private String title;

    /**
     * 最大高度
     */
    private short maxHeight;

    /**
     * 合并后最后行数
     */
    private int subMergedLastRowNum = 0;

    /**
     * 合并后开始行数
     */
    private int subMergedFirstRowNum = 1;

    /**
     * 对象的子列表方法
     */
    private Method subMethod;

    /**
     * 对象的子列表属性
     */
    private List<Field> subFields;

    /**
     * 统计列表
     */
    private final Map<Integer, Double> statistics = new HashMap<Integer, Double>();

    /**
     * 实体对象
     */
    public Class<T> clazz;

    /**
     * 需要显示列属性
     */
    public String[] includeFields;

    /**
     * 需要排除列属性
     */
    public String[] excludeFields;

    public ExcelUtils(Class<T> clazz) {
        this.clazz = clazz;
    }

    /**
     * 仅在Excel中显示列属性
     *
     * @param fields 列属性名 示例[单个"name"/多个"id","name"]
     */
    public void showColumn(String... fields) {
        this.includeFields = fields;
    }

    /**
     * 隐藏Excel中列属性
     *
     * @param fields 列属性名 示例[单个"name"/多个"id","name"]
     */
    public void hideColumn(String... fields) {
        this.excludeFields = fields;
    }

    public void init(List<T> list, String sheetName, String title, Excel.Type type) {
        if (list == null) {
            list = new ArrayList<T>();
        }
        this.list = list;
        this.sheetName = sheetName;
        this.type = type;
        this.title = title;
        createExcelField();
        createWorkbook();
        createTitle();
        createSubHead();
    }

    /**
     * 创建excel第一行标题
     */
    public void createTitle() {
        if (StrUtil.isNotEmpty(title)) {
            int titleLastCol = this.fields.size() - 1;
            if (isSubList()) {
                titleLastCol = titleLastCol + subFields.size() - 1;
            }
            Row titleRow = sheet.createRow(rowNum == 0 ? rowNum++ : 0);
            titleRow.setHeightInPoints(30);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle(styles.get("title"));
            titleCell.setCellValue(title);
            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));
        }
    }

    /**
     * 创建对象的子列表名称
     */
    public void createSubHead() {
        if (isSubList()) {
            Row subRow = sheet.createRow(rowNum);
            int column = 0;
            int subFieldSize = subFields != null ? subFields.size() : 0;
            for (Object[] objects : fields) {
                Field field = (Field) objects[0];
                Excel attr = (Excel) objects[1];
                if (Collection.class.isAssignableFrom(field.getType())) {
                    Cell cell = subRow.createCell(column);
                    cell.setCellValue(attr.name());
                    cell.setCellStyle(styles.get(String.format("header_%s_%s", attr.headerColor(), attr.headerBackgroundColor())));
                    if (subFieldSize > 1) {
                        CellRangeAddress cellAddress = new CellRangeAddress(rowNum, rowNum, column, column + subFieldSize - 1);
                        sheet.addMergedRegion(cellAddress);
                    }
                    column += subFieldSize;
                } else {
                    Cell cell = subRow.createCell(column++);
                    cell.setCellValue(attr.name());
                    cell.setCellStyle(styles.get(String.format("header_%s_%s", attr.headerColor(), attr.headerBackgroundColor())));
                }
            }
            rowNum++;
        }
    }

    /**
     * 对excel表单默认第一个索引名转换成list
     *
     * @param is 输入流
     * @return 转换后集合
     */
    public List<T> importExcel(InputStream is) {
        return importExcel(is, 0);
    }

    /**
     * 对excel表单默认第一个索引名转换成list
     *
     * @param is       输入流
     * @param titleNum 标题占用行数
     * @return 转换后集合
     */
    public List<T> importExcel(InputStream is, int titleNum) {
        List<T> list = null;
        try {
            list = importExcel(StrUtil.EMPTY, is, titleNum);
        } catch (Exception e) {
            log.error("导入Excel异常{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(is);
        }
        return list;
    }

    /**
     * 对excel表单指定表格索引名转换成list
     *
     * @param sheetName 表格索引名
     * @param titleNum  标题占用行数
     * @param is        输入流
     * @return 转换后集合
     */
    public List<T>importExcel(String sheetName, InputStream is, int titleNum) throws Exception {
        this.type = Excel.Type.IMPORT;
        this.wb = WorkbookFactory.create(is);
        List<T> list = new ArrayList<T>();
        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
        Sheet sheet = StrUtil.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
        if (sheet == null) {
            throw new IOException("文件sheet不存在");
        }

        // 获取最后一个非空行的行下标，比如总行数为n，则返回的为n-1
        int rows = sheet.getLastRowNum();
        if (rows > 0) {
            // 定义一个map用于存放excel列的序号和field.
            Map<String, Integer> cellMap = new HashMap<String, Integer>();
            // 获取表头
            Row heard = sheet.getRow(titleNum);
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {
                Cell cell = heard.getCell(i);
                if (cell != null) {
                    String value = this.getCellValue(heard, i).toString();
                    cellMap.put(value, i);
                } else {
                    cellMap.put(null, i);
                }
            }
            // 有数据时才处理 得到类的所有field.
            List<Object[]> fields = this.getFields();
            Map<Integer, Object[]> fieldsMap = new HashMap<>();
            for (Object[] objects : fields) {
                Excel attr = (Excel) objects[1];
                Integer column = cellMap.get(attr.name());
                if (column != null) {
                    fieldsMap.put(column, objects);
                }
            }
            for (int i = titleNum + 1; i <= rows; i++) {
                // 从第2行开始取数据,默认第一行是表头.
                Row row = sheet.getRow(i);
                // 判断当前行是否是空行
                if (isRowEmpty(row)) {
                    continue;
                }
                T entity = null;
                for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) {
                    Object val = this.getCellValue(row, entry.getKey());

                    // 如果不存在实例则新建.
                    entity = (entity == null ? clazz.newInstance() : entity);
                    // 从map中得到对应列的field.
                    Field field = (Field) entry.getValue()[0];
                    Excel attr = (Excel) entry.getValue()[1];
                    // 取得类型,并根据对象类型设置值.
                    Class<?> fieldType = field.getType();
                    if (String.class == fieldType) {
                        String s = CellValueConvert.toStr(val);
                        if (s.matches("^\\d+\\.0$")) {
                            val = StringUtils.substringBefore(s, ".0");
                        } else {
                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
                            if (StrUtil.isNotEmpty(dateFormat)) {
                                val = parseDateToStr(dateFormat, val);
                            } else {
                                val = CellValueConvert.toStr(val);
                            }
                        }
                    } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(CellValueConvert.toStr(val))) {
                        val = CellValueConvert.toInt(val);
                    } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(CellValueConvert.toStr(val))) {
                        val = CellValueConvert.toLong(val);
                    } else if (Double.TYPE == fieldType || Double.class == fieldType) {
                        val = CellValueConvert.toDouble(val);
                    } else if (Float.TYPE == fieldType || Float.class == fieldType) {
                        val = CellValueConvert.toFloat(val);
                    } else if (BigDecimal.class == fieldType) {
                        val = CellValueConvert.toBigDecimal(val);
                    } else if (Date.class == fieldType) {
                        if (val instanceof String) {
                            val = ExcelDateUtils.parseDate(val);
                        } else if (val instanceof Double) {
                            val = DateUtil.getJavaDate((Double) val);
                        }
                    } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
                        val = CellValueConvert.toBool(val, false);
                    }
                    if (fieldType != null) {
                        String propertyName = field.getName();
                        if (StrUtil.isNotEmpty(attr.targetAttr())) {
                            propertyName = field.getName() + "." + attr.targetAttr();
                        }
                        if (StrUtil.isNotEmpty(attr.fixDataConverter())) {

                            if (this.type == Excel.Type.EXPORT && attr.isOpenExportDataConverter()) {
                                val = reverseByExp(CellValueConvert.toStr(val), attr.fixDataConverter(), attr.separator());
                            } else if (this.type == Excel.Type.IMPORT && attr.isOpenImportDataConverter()) {
                                val = reverseByExp(CellValueConvert.toStr(val), attr.fixDataConverter(), attr.separator());
                            } else {

                            }

                        } else if (!attr.selfDataConversionAdapter().equals(SelfDataConversionAdapter.class)
                                && SelfDataConversionAdapter.class.isAssignableFrom(attr.selfDataConversionAdapter())
                        ) {
                            if (this.type == Excel.Type.EXPORT && attr.isOpenExportDataConverter()) {
                                val = dataFormatHandlerAdapter(this.type, val, attr, null);
                            } else if (this.type == Excel.Type.IMPORT && attr.isOpenImportDataConverter()) {
                                val = dataFormatHandlerAdapter(this.type, val, attr, null);
                            } else {

                            }
                        } else if (Excel.ColumnType.IMAGE == attr.cellType()) {
                            // 处理图片导入：从单元格获取图片数据并转换为 Base64 或 URL
                            val = importImageFromCell(row.getCell(entry.getKey()), attr);
                        } else if (Excel.ColumnType.LINK == attr.cellType()) {
                            // 处理链接导入：从单元格获取超链接地址
                            val = importLinkFromCell(row.getCell(entry.getKey()), attr);
                        }
                        ReflectUtils.invokeSetter(entity, propertyName, val);
                    }
                }
                list.add(entity);
            }
        }
        return list;
    }


    /**
     * 设置导出 Excel 的响应头
     *
     * @param response HTTP 响应对象
     * @param fileName 文件名称
     */
    public static void setResponseHeader(HttpServletResponse response, String fileName) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");

        // 设置禁止缓存，防止敏感数据泄露
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "0");

        // 防止 MIME 类型嗅探
        response.setHeader("X-Content-Type-Options", "nosniff");

        if (StrUtil.isEmpty(fileName)) {
            fileName = "Excel_" + java.time.LocalDateTime.now().format(
                    java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx";
        }
        try {
            // 根据 RFC 5987 标准编码文件名，兼容各种浏览器
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())
                    .replaceAll("\\+", "%20")
                    .replaceAll("\"", "%22");
            response.setHeader("Content-disposition", "attachment; filename*=UTF-8''" + encodedFileName);
        } catch (Exception e) {
            // 如果编码失败，使用默认文件名
            response.setHeader("Content-disposition", "attachment; filename=export.xlsx");
        }
    }


    /**
     * 对 list 数据源将其里面的数据导入到 excel 表单
     *
     * @param response  返回数据
     * @param fileName  文件名称
     * @param sheetName 工作表的名称
     * @param title     标题
     * @param list      导出数据集合
     */
    public void exportExcel(HttpServletResponse response, String fileName, String sheetName, String title, List<T> list) {
        setResponseHeader(response, fileName);
        this.init(list, sheetName, title, Excel.Type.EXPORT);
        exportExcel(response);
    }


    /**
     * 对 list 数据源将其里面的数据导入到 excel 表单
     *
     * @param sheetName 工作表的名称
     * @return 结果
     */
    public void exportExcel(HttpServletResponse response, String fileName, String sheetName, List<T> example) {
        setResponseHeader(response, fileName);
        this.init(example, sheetName, title, Excel.Type.EXPORT);
        exportExcel(response);
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     */
    private void exportExcel(HttpServletResponse response) {
        try {
            writeSheet();
            wb.write(response.getOutputStream());
        } catch (Exception e) {
            log.error("导出Excel异常{}", e.getMessage());
        } finally {
            IOUtils.closeQuietly(wb);
        }
    }

    /**
     * 创建写入数据到Sheet
     */
    public void writeSheet() {
        // 取出一共有多少个sheet.
        int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
        for (int index = 0; index < sheetNo; index++) {
            createSheet(sheetNo, index);

            // 产生一行
            Row row = sheet.createRow(rowNum);
            int column = 0;
            // 写入各个字段的列头名称
            for (Object[] os : fields) {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                if (Collection.class.isAssignableFrom(field.getType())) {
                    for (Field subField : subFields) {
                        Excel subExcel = subField.getAnnotation(Excel.class);
                        this.createHeadCell(subExcel, row, column++);
                    }
                } else {
                    this.createHeadCell(excel, row, column++);
                }
            }
            if (Excel.Type.ALL.equals(type) || Excel.Type.EXPORT.equals(type)) {
                fillExcelData(index, row);
                addStatisticsRow();
            }
        }
    }

    /**
     * 填充excel数据
     *
     * @param index 序号
     * @param row   单元格行
     */
    @SuppressWarnings("unchecked")
    public void fillExcelData(int index, Row row) {
        int startNo = index * sheetSize;
        int endNo = Math.min(startNo + sheetSize, list.size());
        int currentRowNum = rowNum + 1; // 从标题行后开始

        for (int i = startNo; i < endNo; i++) {
            row = sheet.createRow(currentRowNum);
            T vo = (T) list.get(i);
            int column = 0;
            int maxSubListSize = getCurrentMaxSubListSize(vo);
            for (Object[] os : fields) {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                if (Collection.class.isAssignableFrom(field.getType())) {
                    try {
                        Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
                        if (subList != null && !subList.isEmpty()) {
                            int subIndex = 0;
                            for (Object subVo : subList) {
                                Row subRow = sheet.getRow(currentRowNum + subIndex);
                                if (subRow == null) {
                                    subRow = sheet.createRow(currentRowNum + subIndex);
                                }

                                int subColumn = column;
                                for (Field subField : subFields) {
                                    Excel subExcel = subField.getAnnotation(Excel.class);
                                    addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
                                }
                                subIndex++;
                            }
                            column += subFields.size();
                        }
                    } catch (Exception e) {
                        log.error("填充集合数据失败", e);
                    }
                } else {
                    // 创建单元格并设置值
                    addCell(excel, row, vo, field, column);
                    if (maxSubListSize > 1 && excel.needMerge()) {
                        sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column));
                    }
                    column++;
                }
            }
            currentRowNum += maxSubListSize;
        }
    }

    /**
     * 获取子列表最大数
     */
    private int getCurrentMaxSubListSize(T vo) {
        int maxSubListSize = 1;
        for (Object[] os : fields) {
            Field field = (Field) os[0];
            if (Collection.class.isAssignableFrom(field.getType())) {
                try {
                    Collection<?> subList = (Collection<?>) getTargetValue(vo, field, (Excel) os[1]);
                    if (subList != null && !subList.isEmpty()) {
                        maxSubListSize = Math.max(maxSubListSize, subList.size());
                    }
                } catch (Exception e) {
                    log.error("获取集合大小失败", e);
                }
            }
        }
        return maxSubListSize;
    }

    /**
     * 创建表格样式
     *
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(Workbook wb) {
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 16);
        titleFont.setBold(true);
        style.setFont(titleFont);
        DataFormat dataFormat = wb.createDataFormat();
        style.setDataFormat(dataFormat.getFormat("@"));
        styles.put("title", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setDataFormat(dataFormat.getFormat("######0.00"));
        Font totalFont = wb.createFont();
        totalFont.setFontName("Arial");
        totalFont.setFontHeightInPoints((short) 10);
        style.setFont(totalFont);
        styles.put("total", style);

        styles.putAll(annotationHeaderStyles(wb, styles));

        styles.putAll(annotationDataStyles(wb));

        return styles;
    }

    /**
     * 根据Excel注解创建表格头样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles) {
        Map<String, CellStyle> headerStyles = new HashMap<>();
        for (Object[] os : fields) {
            Excel excel = (Excel) os[1];
            String key = String.format("header_%s_%s", excel.headerColor(), excel.headerBackgroundColor());
            if (!headerStyles.containsKey(key)) {
                CellStyle style = wb.createCellStyle();
                style.cloneStyleFrom(styles.get("data"));
                style.setAlignment(HorizontalAlignment.CENTER);
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                style.setFillForegroundColor(excel.headerBackgroundColor().index);
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                Font headerFont = wb.createFont();
                headerFont.setFontName("Arial");
                headerFont.setFontHeightInPoints((short) 10);
                headerFont.setBold(true);
                headerFont.setColor(excel.headerColor().index);
                style.setFont(headerFont);
                // 设置表格头单元格文本形式
                DataFormat dataFormat = wb.createDataFormat();
                style.setDataFormat(dataFormat.getFormat("@"));
                headerStyles.put(key, style);
            }
        }
        return headerStyles;
    }

    /**
     * 根据Excel注解创建表格列样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> annotationDataStyles(Workbook wb) {
        Map<String, CellStyle> styles = new HashMap<>();
        for (Object[] os : fields) {
            Field field = (Field) os[0];
            Excel excel = (Excel) os[1];
            if (Collection.class.isAssignableFrom(field.getType())) {
                ParameterizedType pt = (ParameterizedType) field.getGenericType();
                Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
                for (Field subField : subFields) {
                    Excel subExcel = subField.getAnnotation(Excel.class);
                    annotationDataStyles(styles, subField, subExcel);
                }
            } else {
                annotationDataStyles(styles, field, excel);
            }
        }
        return styles;
    }

    /**
     * 根据Excel注解创建表格列样式
     *
     * @param styles 自定义样式列表
     * @param field  属性列信息
     * @param excel  注解信息
     */
    public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel) {
        String key = String.format("data_%s_%s_%s_%s_%s", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText());
        if (!styles.containsKey(key)) {
            CellStyle style = wb.createCellStyle();
            style.setAlignment(excel.align());
            style.setVerticalAlignment(VerticalAlignment.CENTER);
            style.setBorderRight(BorderStyle.THIN);
            style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setBorderLeft(BorderStyle.THIN);
            style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setBorderTop(BorderStyle.THIN);
            style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setBorderBottom(BorderStyle.THIN);
            style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            style.setFillForegroundColor(excel.backgroundColor().getIndex());
            style.setWrapText(excel.wrapText());
            Font dataFont = wb.createFont();
            dataFont.setFontName("Arial");
            dataFont.setFontHeightInPoints((short) 10);
            dataFont.setColor(excel.color().index);
            style.setFont(dataFont);
            if (Excel.ColumnType.TEXT == excel.cellType()) {
                DataFormat dataFormat = wb.createDataFormat();
                style.setDataFormat(dataFormat.getFormat("@"));
            }
            styles.put(key, style);
        }
    }

    /**
     * 创建单元格
     */
    public Cell createHeadCell(Excel attr, Row row, int column) {
        // 创建列
        Cell cell = row.createCell(column);
        // 写入列信息
        cell.setCellValue(attr.name());
        setDataValidation(attr, row, column);
        cell.setCellStyle(styles.get(String.format("header_%s_%s", attr.headerColor(), attr.headerBackgroundColor())));
        if (isSubList()) {
            // 填充默认样式，防止合并单元格样式失效
            sheet.setDefaultColumnStyle(column, styles.get(String.format("data_%s_%s_%s_%s_%s", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
            if (attr.needMerge()) {
                sheet.addMergedRegion(new CellRangeAddress(rowNum - 1, rowNum, column, column));
            }
        }
        return cell;
    }
    /**
     * 设置单元格信息
     *
     * @param value 单元格值
     * @param attr  注解相关
     * @param cell  单元格信息
     * @param obj   当前对象实例
     */
    public void setCellVo(Object value, Excel attr, Cell cell, T obj) {
        if (Excel.ColumnType.STRING == attr.cellType() || Excel.ColumnType.TEXT == attr.cellType()) {
            String cellValue = CellValueConvert.toStr(value);
            // 对于任何以表达式触发字符 =-+@开头的单元格，直接使用 tab 字符作为前缀，防止 CSV 注入。
            if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) {
                cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
            }
            if (value instanceof Collection && StringUtils.equals("[]", cellValue)) {
                cellValue = StringUtils.EMPTY;
            }
            cell.setCellValue(StringUtils.isEmpty(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
        } else if (Excel.ColumnType.NUMERIC == attr.cellType()) {
            if (value != null) {
                cell.setCellValue(StringUtils.contains(CellValueConvert.toStr(value), ".") ? CellValueConvert.toDouble(value) : CellValueConvert.toInt(value));
            }
        } else if (Excel.ColumnType.IMAGE == attr.cellType()) {
            String imagePath = CellValueConvert.toStr(value);
            if (StringUtils.isNotEmpty(imagePath)) {
                byte[] data = ImageUtils.getImage(imagePath);
                int pictureIndex = cell.getSheet().getWorkbook().addPicture(data, getImageType(data));

                // 如果配置了图片链接，先设置单元格内容和超链接

                // 没有链接时，图片可以占满整个单元格
                ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0,
                        (short) cell.getColumnIndex(), cell.getRow().getRowNum(),
                        (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);

                Picture picture = getDrawingPatriarch(cell.getSheet()).createPicture(anchor, pictureIndex);

                // 调整行高和列宽：优先使用注解配置的 height，其次使用默认值
                Row row = cell.getRow();
                double defaultHeight = attr.height() > 0 ? attr.height() : 30; // 默认 50 点，约 10.58 毫米
                if (row.getHeight() < defaultHeight * 20) {
                    row.setHeightInPoints((float) (defaultHeight * 20));
                }
                // 列宽优先使用注解配置的 width，其次使用默认值
                int columnWidth = attr.width() > 0 ? (int) ((attr.width() + 0.72) * 256) : (15 * 256);
                cell.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth);

            }
        } else if (Excel.ColumnType.LINK == attr.cellType()) {
            // 处理超链接
            String linkUrl = CellValueConvert.toStr(value);
            if (StringUtils.isNotEmpty(linkUrl)) {
                // 根据注解的 hyperlinkType 创建对应的超链接
                org.apache.poi.ss.usermodel.Hyperlink hyperlink = null;
                switch (attr.hyperlinkType()) {
                    case URL:
                        hyperlink = cell.getSheet().getWorkbook().getCreationHelper().createHyperlink(HyperlinkType.URL);
                        hyperlink.setAddress(linkUrl);
                        break;
                    case EMAIL:
                        hyperlink = cell.getSheet().getWorkbook().getCreationHelper().createHyperlink(HyperlinkType.EMAIL);
                        hyperlink.setAddress(linkUrl.startsWith("mailto:") ? linkUrl : "mailto:" + linkUrl);
                        break;
                    case FILE:
                        hyperlink = cell.getSheet().getWorkbook().getCreationHelper().createHyperlink(HyperlinkType.FILE);
                        hyperlink.setAddress(linkUrl);
                        break;
                    case DOCUMENT:
                        hyperlink = cell.getSheet().getWorkbook().getCreationHelper().createHyperlink(HyperlinkType.DOCUMENT);
                        hyperlink.setAddress(linkUrl);
                        break;
                    default:
                        // 如果没有指定类型，默认为 URL
                        hyperlink = cell.getSheet().getWorkbook().getCreationHelper().createHyperlink(HyperlinkType.URL);
                        hyperlink.setAddress(linkUrl);
                        break;
                }
                cell.setHyperlink(hyperlink);

                // 设置链接显示文本：优先使用 linkTextField 指定的字段值，其次使用 linkText，最后显示 URL 本身
                String displayText = getLinkDisplayText(obj, attr, linkUrl);
                cell.setCellValue(displayText);

                // 设置超链接样式（蓝色字体、下划线）
                CellStyle linkStyle = cell.getCellStyle();
                if (linkStyle == null) {
                    linkStyle = cell.getSheet().getWorkbook().createCellStyle();
                }
                Font linkFont = cell.getSheet().getWorkbook().createFont();
                linkFont.setColor(IndexedColors.BLUE.getIndex());
                linkFont.setUnderline(Font.U_SINGLE);
                linkStyle.setFont(linkFont);
                cell.setCellStyle(linkStyle);
            }
        }
    }

    /**
     * 获取链接的显示文本
     * 优先级：linkTextField > linkText > URL 本身
     *
     * @param obj     当前对象实例
     * @param attr    Excel 注解
     * @param linkUrl 链接地址
     * @return 链接显示文本
     */
    private String getLinkDisplayText(T obj, Excel attr, String linkUrl) {
        // 优先使用 linkTextField 指定的字段值
        if (StringUtils.isNotEmpty(attr.linkTextField())) {
            try {
                Field textField = ReflectUtils.getAccessibleField(obj, attr.linkTextField());
                if (textField != null) {
                    textField.setAccessible(true);
                    Object textValue = textField.get(obj);
                    if (textValue != null && StringUtils.isNotEmpty(textValue.toString())) {
                        return textValue.toString();
                    }
                }
            } catch (Exception e) {
                log.warn("获取链接显示文本失败，字段名：{}", attr.linkTextField());
            }
        }

        // 其次使用 linkText
        if (StringUtils.isNotEmpty(attr.linkText())) {
            return attr.linkText();
        }

        // 最后使用 URL 本身
        return linkUrl;
    }

    /**
     * 获取画布
     */
    public static Drawing<?> getDrawingPatriarch(Sheet sheet) {
        if (sheet.getDrawingPatriarch() == null) {
            sheet.createDrawingPatriarch();
        }
        return sheet.getDrawingPatriarch();
    }

    /**
     * 获取图片类型,设置图片插入类型
     */
    public int getImageType(byte[] value) {
        String type = getFileExtendName(value);
        if ("JPG".equalsIgnoreCase(type)) {
            return Workbook.PICTURE_TYPE_JPEG;
        } else if ("PNG".equalsIgnoreCase(type)) {
            return Workbook.PICTURE_TYPE_PNG;
        }
        return Workbook.PICTURE_TYPE_JPEG;
    }

    /**
     * 获取文件类型
     *
     * @param photoByte 文件字节码
     * @return 后缀（不含".")
     */
    public static String getFileExtendName(byte[] photoByte) {
        String strFileExtendName = "JPG";
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
            strFileExtendName = "GIF";
        } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
            strFileExtendName = "JPG";
        } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
            strFileExtendName = "BMP";
        } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
            strFileExtendName = "PNG";
        }
        return strFileExtendName;
    }

    /**
     * 创建表格样式
     */
    public void setDataValidation(Excel attr, Row row, int column) {
        if (attr.name().contains("注：")) {
            sheet.setColumnWidth(column, 6000);
        } else {
            // 设置列宽
            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
        }
        if (attr.combo().length > 0) {
            if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) {
                // 如果下拉数大于15或字符串长度大于255，则使用一个新sheet存储，避免生成的模板下拉值获取不到
                setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 200, column, column);
            } else {
                // 提示信息或只能选择不能输入的列内容.
                setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 200, column, column);
            }
        } else if (StrUtil.isNotEmpty(attr.comboFromBeanMethod())) {
            try {
                ExcelBeanMethodParseVo excelMethodParseVo = parseMethodInvocation(attr.comboFromBeanMethod(), attr.args());
                if (excelMethodParseVo != null) {
                    Object o = invokeMethodFromSpringContext(excelMethodParseVo);
                    if (o == null) {
                        return;
                    }
                    String[] combo = null;
                    if (o instanceof List) {
                        List<?> list = (List<?>) o;
                        combo = list.stream().map(String::valueOf).toArray(String[]::new);
                    } else if (o instanceof String[]) {
                        combo = (String[]) o;
                    } else if (o instanceof String) {
                        // 支持逗号分隔的字符串
                        String str = (String) o;
                        if (StrUtil.isNotEmpty(str)) {
                            combo = str.split(",");
                        }
                    }

                    if (combo != null && combo.length > 0) {
                        if (combo.length > 15 || StringUtils.join(combo).length() > 255) {
                            // 如果下拉数大于 15 或字符串长度大于 255，则使用一个新 sheet 存储，避免生成的模板下拉值获取不到
                            setXSSFValidationWithHidden(sheet, combo, attr.prompt(), 1, 200, column, column);
                        } else {
                            // 提示信息或只能选择不能输入的列内容.
                            setPromptOrValidation(sheet, combo, attr.prompt(), 1, 200, column, column);
                        }
                    } else {
                        log.warn("comboFromBeanMethod 返回的数据为空或格式不正确，返回类型：{}", o.getClass().getName());
                    }

                }
            } catch (Exception e) {
                log.error("请检查comboFromDb属性配置是否正确");
            }
        } else if (StrUtil.isNotEmpty(attr.prompt())) {
            setPromptOrValidation(sheet, new String[]{}, attr.prompt(), 1, 200, column, column);
        }
    }


    /**
     * 通过Spring容器获取Bean并调用方法
     *
     * @param parseVo 解析后的方法调用信息
     * @return 方法执行结果
     */
    public Object invokeMethodFromSpringContext(ExcelBeanMethodParseVo parseVo) {
        try {
            Object serviceBean;
            if (beanFactory != null) {
                serviceBean = beanFactory.getBean(parseVo.getServiceName());
            } else {
                // 根据服务名称获取Bean
                serviceBean = SpringBeanUtils.getBean(parseVo.getServiceName());
            }
            if (serviceBean == null) {
                log.error("不能获取到bean，请检查comboFromBeanMethod属性配置是否正确");
                return null;
            }
            // 获取方法参数类型数组
            Class<?>[] paramTypes = new Class[parseVo.getParameters().length];
            for (int i = 0; i < parseVo.getParameters().length; i++) {
                paramTypes[i] = String.class; // 假设所有参数都是String类型
            }
            // 获取方法
            Method method = serviceBean.getClass().getMethod(parseVo.getMethodName(), paramTypes);
            // 调用方法
            return method.invoke(serviceBean, (Object[]) parseVo.getParameters());
        } catch (Exception e) {
            log.error("调用Spring Bean方法失败: {}", e.getMessage());
            throw new RuntimeException("Failed to invoke method: " + parseVo.getMethodName(), e);
        }
    }


    /**
     * 添加单元格
     */
    public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {
        Cell cell = null;
        try {
            // 设置行高
            row.setHeight(maxHeight);
            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
            if (attr.isExport()) {
                // 创建cell
                cell = row.createCell(column);
                if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) {
                    if (subMergedLastRowNum >= subMergedFirstRowNum) {
                        sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column));
                    }
                }
                cell.setCellStyle(styles.get(String.format("data_%s_%s_%s_%s_%s", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));

                // 用于读取对象中的属性
                Object value = getTargetValue(vo, field, attr);
                String dateFormat = attr.dateFormat();
                String readConverterExp = attr.fixDataConverter();
                String separator = attr.separator();
                if (StrUtil.isNotEmpty(dateFormat) && value != null) {
                    cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat));
                    cell.setCellValue(parseDateToStr(dateFormat, value));
                } else if (StrUtil.isNotEmpty(readConverterExp) && value != null) {
                    if (this.type == Excel.Type.EXPORT && attr.isOpenExportDataConverter()) {
                        cell.setCellValue(convertByExp(CellValueConvert.toStr(value), readConverterExp, separator));
                    } else if (this.type == Excel.Type.IMPORT && attr.isOpenImportDataConverter()) {
                        cell.setCellValue(convertByExp(CellValueConvert.toStr(value), readConverterExp, separator));
                    } else {
                        cell.setCellValue(CellValueConvert.toStr(value));
                    }
                } else if (value instanceof BigDecimal && -1 != attr.scale()) {
                    cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());
                } else if (!attr.selfDataConversionAdapter().equals(SelfDataConversionAdapter.class)) {
                    if (this.type == Excel.Type.EXPORT && attr.isOpenExportDataConverter()) {
                        cell.setCellValue(dataFormatHandlerAdapter(this.type, value, attr, cell));
                    } else if (this.type == Excel.Type.IMPORT && attr.isOpenImportDataConverter()) {
                        cell.setCellValue(dataFormatHandlerAdapter(this.type, value, attr, cell));
                    } else {
                        cell.setCellValue(CellValueConvert.toStr(value));
                    }
                } else {
                    // 设置列类型
                    setCellVo(value, attr, cell, vo);
                }
                addStatisticsData(column, CellValueConvert.toStr(value), attr);
            }
        } catch (Exception e) {
            log.error("导出Excel失败{}", e);
        }
        return cell;
    }

    /**
     * 设置 POI XSSFSheet 单元格提示或选择框
     *
     * @param sheet         表单
     * @param textlist      下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstRow      开始行
     * @param endRow        结束行
     * @param firstCol      开始列
     * @param endCol        结束列
     */
    public static void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1");
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StrUtil.isNotEmpty(promptContent)) {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation) {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        } else {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
    }

    /**
     * 设置某些列的值只能输入预制的数据,显示下拉框（兼容超出一定数量的下拉框）.
     *
     * @param sheet         要设置的sheet.
     * @param textList      下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstRow      开始行
     * @param endRow        结束行
     * @param firstCol      开始列
     * @param endCol        结束列
     */
    public void setXSSFValidationWithHidden(Sheet sheet, String[] textList, String promptContent, int firstRow, int endRow, int firstCol, int endCol) {
        String hideSheetName = "combo_" + firstCol + "_" + endCol;
        Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
        for (int i = 0; i < textList.length; i++) {
            hideSheet.createRow(i).createCell(0).setCellValue(textList[i]);
        }
        // 创建名称，可被其他单元格引用
        Name name = wb.createName();
        name.setNameName(hideSheetName + "_data");
        name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textList.length);
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // 加载下拉列表内容
        DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
        // 设置数据有效性加载在哪个单元格上,四个参数分别是：起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        // 数据有效性对象
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StrUtil.isNotEmpty(promptContent)) {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation) {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        } else {
            dataValidation.setSuppressDropDownArrow(false);
        }

        sheet.addValidationData(dataValidation);
        // 设置hiddenSheet隐藏
        wb.setSheetHidden(wb.getSheetIndex(hideSheet), true);
    }

    /**
     * 解析导出值 0=男,1=女,2=未知
     *
     * @param propertyValue 参数值
     * @param converterExp  翻译注解
     * @param separator     分隔符
     * @return 解析后值
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(SEPARATOR);
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }

    /**
     * 反向解析值 男=0,女=1,未知=2
     *
     * @param propertyValue 参数值
     * @param converterExp  翻译注解
     * @param separator     分隔符
     * @return 解析后值
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(SEPARATOR);
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[1].equals(value)) {
                        propertyString.append(itemArray[0]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[1].equals(propertyValue)) {
                    return itemArray[0];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }

    /**
     * 数据处理器
     *
     * @param value 数据值
     * @param excel 数据注解
     * @return
     */
    public String dataFormatHandlerAdapter(Excel.Type op, Object value, Excel excel, Cell cell) {
        try {
            SelfDataConversionAdapter serviceBean;
            if (beanFactory != null) {
                serviceBean = beanFactory.getBean(excel.selfDataConversionAdapter());
            } else {
                // 根据服务名称获取Bean
                serviceBean = SpringBeanUtils.getBean(excel.selfDataConversionAdapter());
            }
            if (serviceBean == null) {
                log.error("不能获取到bean 请检查selfDataConversionAdapter配置：" + excel.selfDataConversionAdapter());
                return "";
            }
            if (op == Excel.Type.EXPORT) {
                value = serviceBean.keyToValueConvert(value, excel.args());
            } else {
                value = serviceBean.valueToKeyConvert(value, excel.args());
            }
        } catch (Exception e) {
            log.error("不能格式化数据 " + excel.selfDataConversionAdapter(), e.getMessage());
        }
        return CellValueConvert.toStr(value);
    }


    /**
     * 合计统计信息
     */
    private void addStatisticsData(Integer index, String text, Excel entity) {
        if (entity != null && entity.isStatistics()) {
            Double temp = 0D;
            if (!statistics.containsKey(index)) {
                statistics.put(index, temp);
            }
            try {
                temp = Double.valueOf(text);
            } catch (NumberFormatException ignored) {
            }
            statistics.put(index, statistics.get(index) + temp);
        }
    }

    /**
     * 创建统计行
     */
    public void addStatisticsRow() {
        if (!statistics.isEmpty()) {
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
            Set<Integer> keys = statistics.keySet();
            Cell cell = row.createCell(0);
            cell.setCellStyle(styles.get("total"));
            cell.setCellValue("合计");

            for (Integer key : keys) {
                cell = row.createCell(key);
                cell.setCellStyle(styles.get("total"));
                cell.setCellValue(statistics.get(key));
            }
            statistics.clear();
        }
    }

    /**
     * 获取bean中的属性值
     *
     * @param vo    实体对象
     * @param field 字段
     * @param excel 注解
     * @return 最终的属性值
     * @throws Exception
     */
    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception {
        field.setAccessible(true);
        Object o = field.get(vo);
        if (StrUtil.isNotEmpty(excel.targetAttr())) {
            String target = excel.targetAttr();
            if (target.contains(".")) {
                String[] targets = target.split("[.]");
                for (String name : targets) {
                    o = getValue(o, name);
                }
            } else {
                o = getValue(o, target);
            }
        }
        return o;
    }

    /**
     * 以类的属性的get方法方法形式获取值
     *
     * @param o
     * @param name
     * @return value
     * @throws Exception
     */
    private Object getValue(Object o, String name) throws Exception {
        if (o != null && StrUtil.isNotEmpty(name)) {
            Class<?> clazz = o.getClass();
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            o = field.get(o);
        }
        return o;
    }

    /**
     * 得到所有定义字段
     */
    private void createExcelField() {
        this.fields = getFields();
        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
        this.maxHeight = getRowHeight();
    }

    /**
     * 获取字段注解信息
     */
    public List<Object[]> getFields() {
        List<Object[]> fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        if (includeFields != null && includeFields.length > 0) {
            for (Field field : tempFields) {
                if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)) {
                    addField(fields, field);
                }
            }
        } else if (excludeFields != null && excludeFields.length > 0) {
            for (Field field : tempFields) {
                if (!ArrayUtils.contains(this.excludeFields, field.getName())) {
                    addField(fields, field);
                }
            }
        } else {
            for (Field field : tempFields) {
                addField(fields, field);
            }
        }
        return fields;
    }

    /**
     * 添加字段信息
     */
    public void addField(List<Object[]> fields, Field field) {
        // 单注解
        if (field.isAnnotationPresent(Excel.class)) {
            Excel attr = field.getAnnotation(Excel.class);
            if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type)) {
                fields.add(new Object[]{field, attr});
            }
            if (Collection.class.isAssignableFrom(field.getType())) {
                subMethod = getSubMethod(field.getName(), clazz);
                ParameterizedType pt = (ParameterizedType) field.getGenericType();
                Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
            }
        }

        // 多注解
        if (field.isAnnotationPresent(Excels.class)) {
            Excels attrs = field.getAnnotation(Excels.class);
            Excel[] excels = attrs.value();
            for (Excel attr : excels) {
                if (includeFields != null && includeFields.length > 0) {
                    if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr()) && (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type))) {
                        fields.add(new Object[]{field, attr});
                    }
                } else {
                    if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) && (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type))) {
                        fields.add(new Object[]{field, attr});
                    }
                }
            }
        }
    }

    /**
     * 根据注解获取最大行高
     */
    public short getRowHeight() {
        double maxHeight = 0;
        for (Object[] os : this.fields) {
            Excel excel = (Excel) os[1];
            maxHeight = Math.max(maxHeight, excel.height());
        }
        return (short) (maxHeight * 20);
    }

    /**
     * 创建一个工作簿
     */
    public void createWorkbook() {
        this.wb = new SXSSFWorkbook(500);
        this.sheet = wb.createSheet();
        wb.setSheetName(0, sheetName);
        this.styles = createStyles(wb);
    }

    /**
     * 创建工作表
     *
     * @param sheetNo sheet数量
     * @param index   序号
     */
    public void createSheet(int sheetNo, int index) {
        // 设置工作表的名称.
        if (sheetNo > 1 && index > 0) {
            this.sheet = wb.createSheet();
            this.createTitle();
            wb.setSheetName(index, sheetName + index);
        }
    }

    /**
     * 获取单元格值
     *
     * @param row    获取的行
     * @param column 获取单元格列号
     * @return 单元格值
     */
    public Object getCellValue(Row row, int column) {
        if (row == null) {
            return row;
        }
        Object val = "";
        try {
            Cell cell = row.getCell(column);
            if (cell != null) {
                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) {
                    val = cell.getNumericCellValue();
                    if (DateUtil.isCellDateFormatted(cell)) {
                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
                    } else {
                        if ((Double) val % 1 != 0) {
                            val = new BigDecimal(val.toString());
                        } else {
                            val = new DecimalFormat("0").format(val);
                        }
                    }
                } else if (cell.getCellType() == CellType.STRING) {
                    val = cell.getStringCellValue();
                } else if (cell.getCellType() == CellType.BOOLEAN) {
                    val = cell.getBooleanCellValue();
                } else if (cell.getCellType() == CellType.ERROR) {
                    val = cell.getErrorCellValue();
                }

            }
        } catch (Exception e) {
            return val;
        }
        return val;
    }

    /**
     * 判断是否是空行
     *
     * @param row 判断的行
     * @return
     */
    private boolean isRowEmpty(Row row) {
        if (row == null) {
            return true;
        }
        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            if (cell != null && cell.getCellType() != CellType.BLANK) {
                return false;
            }
        }
        return true;
    }

    /**
     * 格式化不同类型的日期对象
     *
     * @param dateFormat 日期格式
     * @param val        被格式化的日期对象
     * @return 格式化后的日期字符
     */
    public String parseDateToStr(String dateFormat, Object val) {
        if (val == null) {
            return "";
        }
        String str;
        if (val instanceof Date) {
            str = ExcelDateUtils.parseDateToStr(dateFormat, (Date) val);
        } else if (val instanceof LocalDateTime) {
            str = ExcelDateUtils.parseDateToStr(dateFormat, ExcelDateUtils.toDate((LocalDateTime) val));
        } else if (val instanceof LocalDate) {
            str = ExcelDateUtils.parseDateToStr(dateFormat, ExcelDateUtils.toDate((LocalDate) val));
        } else {
            str = val.toString();
        }
        return str;
    }

    /**
     * 是否有对象的子列表
     */
    public boolean isSubList() {
        return subFields != null && !subFields.isEmpty();
    }

    /**
     * 是否有对象的子列表，集合不为空
     */
    public boolean isSubListValue(T vo) {
        return subFields != null && !subFields.isEmpty() && getListCellValue(vo) != null && !getListCellValue(vo).isEmpty();
    }

    /**
     * 获取集合的值
     */
    public Collection<?> getListCellValue(Object obj) {
        Object value;
        try {
            value = subMethod.invoke(obj, new Object[]{});
        } catch (Exception e) {
            return new ArrayList<Object>();
        }
        return (Collection<?>) value;
    }

    /**
     * 获取对象的子列表方法
     *
     * @param name      名称
     * @param pojoClass 类对象
     * @return 子列表方法
     */
    public Method getSubMethod(String name, Class<?> pojoClass) {
        StringBuilder getMethodName = new StringBuilder("get");
        getMethodName.append(name.substring(0, 1).toUpperCase());
        getMethodName.append(name.substring(1));
        Method method = null;
        try {
            method = pojoClass.getMethod(getMethodName.toString(), new Class[]{});
        } catch (Exception e) {
            log.error("获取对象异常{}", e.getMessage());
        }
        return method;
    }

    /**
     * 解析服务方法调用表达式
     *
     * @param expression 方法调用表达式，格式为：serviceName.methodName() 或 serviceName.methodName('param1') 或 serviceName.methodName('param1', 'param2', ...)
     * @return 解析结果对象
     */
    public ExcelBeanMethodParseVo parseMethodInvocation(String expression, String[] args) {
        // 匹配格式：serviceName.methodName(...) 支持多个参数
        String regex = "(\\w+)\\.(\\w+)\\((.*)\\)";
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regex);
        java.util.regex.Matcher matcher = pattern.matcher(expression.trim());

        if (matcher.matches()) {
            String serviceName = matcher.group(1);
            //首字母小写
            serviceName = serviceName.substring(0, 1).toLowerCase() + serviceName.substring(1);
            String methodName = matcher.group(2);
            String paramsStr = matcher.group(3);

            String[] parameters = new String[0];
            if (paramsStr != null && !paramsStr.isEmpty()) {
                // 解析参数，支持多个参数
                parameters = parseParameters(paramsStr);
            }
            if (args.length > 0) {
                parameters = args;
            }

            return new ExcelBeanMethodParseVo(serviceName, methodName, parameters);
        }

        throw new IllegalArgumentException("Invalid method invocation expression: " + expression);
    }

    /**
     * 解析参数字符串
     *
     * @param paramsStr 参数字符串，如 "'param1'" 或 "'param1', 'param2'"
     * @return 参数数组
     */
    private String[] parseParameters(String paramsStr) {
        // 匹配单引号内的参数
        java.util.regex.Pattern paramPattern = java.util.regex.Pattern.compile("'([^']*)'");
        java.util.regex.Matcher matcher = paramPattern.matcher(paramsStr);

        List<String> parameters = new ArrayList<>();
        while (matcher.find()) {
            parameters.add(matcher.group(1));
        }

        return parameters.toArray(new String[0]);
    }

    /**
     * 从 Excel 单元格导入图片数据
     *
     * @param cell 单元格对象
     * @param attr Excel 注解
     * @return 图片 URL 或 Base64 字符串
     */
    /**
     * 从 Excel 单元格导入图片
     * 支持两种场景：
     * 1. 浮动在单元格上方的图片（通过 Drawing 获取）
     * 2. 嵌入单元格内的图片（通过 Cell 的 RichTextString 或公式获取）
     *
     * @param cell 单元格对象
     * @param attr Excel 注解
     * @return 图片的 Base64 字符串
     */
    private String importImageFromCell(Cell cell, Excel attr) {
        if (cell == null) {
            return "";
        }

        try {
            // 尝试 1: 先检查是否是嵌入单元格的图片（Excel 365+ 的新特性）
            byte[] embeddedImageData = getEmbeddedImageFromCell(cell);
            if (embeddedImageData != null && embeddedImageData.length > 0) {
                return "data:image/png;base64," + Base64.getEncoder().encodeToString(embeddedImageData);
            }

            // 尝试 2: 获取浮动在单元格上方的图片（传统方式）
            Sheet sheet = cell.getSheet();
            Workbook workbook = sheet.getWorkbook();
            int rowNum = cell.getRowIndex();
            int colNum = cell.getColumnIndex();

            // 从指定单元格获取图片数据
            byte[] imageData = getPictureDataFromCell(sheet, rowNum, colNum, workbook);

            if (imageData != null && imageData.length > 0) {
                return "data:image/png;base64," + Base64.getEncoder().encodeToString(imageData);
            }

        } catch (Exception e) {
            log.error("导入图片失败：{}", e.getMessage());
        }

        return "";
    }

    /**
     * 从单元格获取嵌入的图片数据（针对 Excel 365+ 的嵌入单元格图片）
     * 这种方法适用于图片被直接嵌入到单元格内部的情况
     *
     * @param cell 单元格对象
     * @return 图片字节数组，如果没有找到则返回 null
     */
    private byte[] getEmbeddedImageFromCell(Cell cell) {
        // 注意：不捕获最外层异常，让调用方知道是否发生了严重错误

        // 1. 检查单元格的 RichTextString 是否包含图片（优先检查非公式类型）
        CellType cellType = cell.getCellType();
        if (cellType == CellType.STRING) {
            try {
                RichTextString richTextString = cell.getRichStringCellValue();
                if (richTextString != null) {
                    String stringValue = richTextString.getString();
                    // 如果字符串看起来像图片数据（Base64 编码），尝试解码
                    if (stringValue != null && !stringValue.isEmpty()
                            && (stringValue.startsWith("data:image/") || stringValue.startsWith("R0lGOD"))) {
                        // 提取 Base64 部分
                        if (stringValue.startsWith("data:image/")) {
                            int commaIndex = stringValue.indexOf(',');
                            if (commaIndex > 0) {
                                String base64Data = stringValue.substring(commaIndex + 1);
                                return Base64.getDecoder().decode(base64Data);
                            }
                        } else {
                            // 直接是 Base64 数据
                            return Base64.getDecoder().decode(stringValue);
                        }
                    }
                }
            } catch (Exception e) {
                log.warn("读取富文本失败：单元格={}, 错误={}",
                        cell.getAddress().formatAsString(), e.getMessage());
            }
        }

        // 2. 如果是公式类型，尝试计算公式获取图片 URL
        if (cellType == CellType.FORMULA) {
            try {
                String formula = cell.getCellFormula();
                log.debug("检测到公式：{}，单元格：{}", formula, cell.getAddress().formatAsString());

                // 特殊处理：如果是 WPS 的 DISPIMG 公式或 Excel 的 IMAGE 公式，直接从 Drawing 获取
                // 因为这些公式无法通过 POI 计算
                // 注意：需要同时检查 _xlfn.DISPIMG（Excel 内部函数前缀）和 DISPIMG
                if (formula.toUpperCase().contains("DISPIMG(") || formula.toUpperCase().startsWith("IMAGE(")) {
                    log.debug("检测到图片嵌入公式，将从 Drawing 中获取图片：{}", formula);
                } else {
                    // 尝试计算其他类型的公式
                    try {
                        FormulaEvaluator evaluator = cell.getSheet().getWorkbook().getCreationHelper().createFormulaEvaluator();
                        CellValue cellValue = evaluator.evaluate(cell);

                        // 如果公式结果是图片引用，尝试获取
                        if (cellValue.getCellType() == CellType.STRING) {
                            String formulaResult = cellValue.getStringValue();
                            // 检查是否是图片 URL 或路径
                            if (formulaResult != null && !formulaResult.isEmpty()) {
                                // 可以尝试从 URL 加载图片
                                if (formulaResult.startsWith("http://") || formulaResult.startsWith("https://")) {
                                    try {
                                        URL imageUrl = new URL(formulaResult);
                                        return IOUtils.toByteArray(imageUrl.openStream());
                                    } catch (Exception e) {
                                        log.debug("从 URL 加载图片失败：{}", e.getMessage());
                                    }
                                }
                                // 如果公式返回的是 Base64 字符串，也尝试解析
                                if (formulaResult.startsWith("data:image/") || formulaResult.startsWith("R0lGOD")) {
                                    if (formulaResult.startsWith("data:image/")) {
                                        int commaIndex = formulaResult.indexOf(',');
                                        if (commaIndex > 0) {
                                            String base64Data = formulaResult.substring(commaIndex + 1);
                                            return Base64.getDecoder().decode(base64Data);
                                        }
                                    } else {
                                        return Base64.getDecoder().decode(formulaResult);
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        // 公式计算失败不影响后续从 Drawing 获取图片
                        log.debug("公式计算失败，将继续尝试从 Drawing 获取：单元格={}, 错误={}",
                                cell.getAddress().formatAsString(), e.getMessage());
                    }
                }
            } catch (Exception e) {
                log.debug("获取公式内容失败：单元格={}, 错误={}",
                        cell.getAddress().formatAsString(), e.getMessage());
            }
        }

        // 3. 检查单元格是否有注释，注释中可能包含图片
        Comment comment = cell.getCellComment();
        if (comment != null) {
            try {
                // 从单元格的 sheet 的 Drawing 中查找与注释相关的图片
                Sheet sheet = cell.getSheet();
                Drawing<?> drawing = sheet.getDrawingPatriarch();
                if (drawing != null) {
                    for (Object obj : drawing) {
                        if (obj instanceof Picture) {
                            Picture picture = (Picture) obj;
                            ClientAnchor anchor = picture.getClientAnchor();
                            if (anchor != null &&
                                    anchor.getCol1() == cell.getColumnIndex() &&
                                    anchor.getRow1() == cell.getRowIndex()) {
                                return picture.getPictureData().getData();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.debug("读取注释图片失败：单元格={}, 错误={}",
                        cell.getAddress().formatAsString(), e.getMessage());
            }
        }

        // 4. 【核心步骤】从 Drawing 中获取嵌入单元格的图片
        // 这是最可靠的方式，适用于：
        // - WPS 的 DISPIMG 公式（包括 _xlfn.DISPIMG）
        // - Excel 的 IMAGE 公式
        // - 直接插入的图片
        try {
            Sheet sheet = cell.getSheet();
            Drawing<?> drawing = sheet.getDrawingPatriarch();
            if (drawing != null) {
                log.debug("开始从 Drawing 中查找图片，单元格：{}", cell.getAddress().formatAsString());

                for (Object obj : drawing) {
                    if (obj instanceof Picture) {
                        Picture picture = (Picture) obj;
                        ClientAnchor anchor = picture.getClientAnchor();
                        if (anchor != null) {
                            // 获取图片的位置信息
                            int col1 = anchor.getCol1();
                            int row1 = anchor.getRow1();
                            int col2 = anchor.getCol2();
                            int row2 = anchor.getRow2();

                            log.debug("发现图片：起始 ({},{}) 结束 ({},{})",
                                    row1, col1, row2, col2);

                            // 检查图片是否在当前单元格
                            boolean inCurrentCell = (col1 == cell.getColumnIndex() && row1 == cell.getRowIndex());

                            // 或者图片跨越了当前单元格
                            boolean inRange = (col1 <= cell.getColumnIndex() && cell.getColumnIndex() < col2) &&
                                    (row1 <= cell.getRowIndex() && cell.getRowIndex() < row2);

                            if (inCurrentCell || inRange) {
                                log.debug("找到匹配的图片，大小：{} bytes", picture.getPictureData().getData().length);
                                return picture.getPictureData().getData();
                            }
                        }
                    }
                }
                log.debug("在 Drawing 中未找到匹配的图片");
            } else {
                log.debug("Drawing 为空，尝试从 Workbook 所有图片中查找");

                // 5. 【关键】如果 Drawing 为空，从工作簿的所有图片资源中查找
                // 这适用于 _xlfn.DISPIMG、DISPIMG 等内部函数存储的图片
                try {
                    List<? extends PictureData> allPictures = cell.getSheet().getWorkbook().getAllPictures();
                    if (allPictures != null && !allPictures.isEmpty()) {
                        log.debug("发现工作簿中有 {} 张图片", allPictures.size());

                        // 遍历所有图片，找到有效的图片数据
                        for (int i = 0; i < allPictures.size(); i++) {
                            PictureData pictureData = allPictures.get(i);
                            byte[] data = pictureData.getData();
                            if (data != null && data.length > 0) {
                                log.debug("获取到第 {} 张图片数据，大小：{} bytes", i + 1, data.length);
                                // 注意：getAllPictures() 无法确定图片位置，返回第一张有效的图片
                                // 对于 DISPIMG 公式，通常每个单元格对应一张图片
                                return data;
                            }
                        }
                    } else {
                        log.debug("工作簿中没有图片资源");
                    }
                } catch (Exception e) {
                    log.debug("从 Workbook 所有图片中查找失败：{}", e.getMessage());
                }
            }
        } catch (Exception e) {
            log.debug("从 Drawing 获取嵌入图片失败：单元格={}, 错误={}",
                    cell.getAddress().formatAsString(), e.getMessage());
        }

        return null;
    }

    /**
     * 从工作表获取指定单元格的图片列表
     *
     * @param sheet  工作表
     * @param rowNum 行号
     * @param colNum 列号
     * @return 图片列表
     */
    private List<Picture> getPicturesFromSheet(Sheet sheet, int rowNum, int colNum) {
        List<Picture> pictures = new ArrayList<>();
        try {
            Workbook workbook = sheet.getWorkbook();

            // 获取当前 sheet 的索引
            int currentSheetIndex = workbook.getSheetIndex(sheet);

            // 获取 Drawing 中的所有图片（这是最可靠的方法）
            Drawing<?> drawing = sheet.getDrawingPatriarch();
            if (drawing != null) {
                for (Object obj : drawing) {
                    if (obj instanceof Picture) {
                        Picture picture = (Picture) obj;
                        if (isPictureInCell(picture, rowNum, colNum)) {
                            pictures.add(picture);
                        }
                    }
                }
            }

            // 如果 Drawing 中没有找到，尝试从 workbook.getAllPictures() 获取
            // 注意：getAllPictures() 返回的是整个工作簿的所有图片
            if (pictures.isEmpty()) {
                List<? extends PictureData> allPicturesData = workbook.getAllPictures();
                if (allPicturesData != null && !allPicturesData.isEmpty()) {
                    // 遍历所有图片，找到在当前 sheet 且目标单元格的图片
                    for (int i = 0; i < allPicturesData.size(); i++) {
                        PictureData pictureData = allPicturesData.get(i);

                        // 尝试将 PictureData 转换为 Picture（仅 XSSF 支持）
                        if (pictureData instanceof Picture) {
                            Picture picture = (Picture) pictureData;
                            if (isPictureInCell(picture, rowNum, colNum)) {
                                pictures.add(picture);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("获取图片列表失败：{}", e.getMessage());
        }

        return pictures;
    }

    /**
     * 从 Drawing 中获取指定位置的图片（用于 HSSF）
     */
    private Picture getPictureFromDrawing(Sheet sheet, int pictureIndex, int rowNum, int colNum) {
        try {
            Drawing<?> drawing = sheet.getDrawingPatriarch();
            if (drawing == null) {
                return null;
            }

            // 遍历 Drawing 中的所有对象，找到第 pictureIndex 个 Picture
            int currentIndex = 0;
            for (Object obj : drawing) {
                if (obj instanceof Picture) {
                    if (currentIndex == pictureIndex) {
                        Picture picture = (Picture) obj;
                        if (isPictureInCell(picture, rowNum, colNum)) {
                            return picture;
                        }
                    }
                    currentIndex++;
                }
            }

            return null;
        } catch (Exception e) {
            log.debug("从 Drawing 获取图片失败：{}", e.getMessage());
            return null;
        }
    }


    /**
     * 判断图片是否在指定单元格内
     *
     * @param picture 图片对象
     * @param rowNum  行号
     * @param colNum  列号
     * @return true-在单元格内，false-不在单元格内
     */
    private boolean isPictureInCell(Picture picture, int rowNum, int colNum) {
        try {
            // 获取图片的锚点
            ClientAnchor anchor = picture.getClientAnchor();
            if (anchor != null) {
                int pictureRow1 = anchor.getRow1();
                int pictureCol1 = anchor.getCol1();

                // 打印调试信息（仅用于测试）
                log.debug("图片位置 - Row1: {}, Col1: {} | 目标位置 - Row: {}, Col: {}",
                        pictureRow1, pictureCol1, rowNum, colNum);

                // 简单匹配：图片左上角是否在目标单元格
                return (pictureRow1 == rowNum && pictureCol1 == colNum);
            }
        } catch (Exception e) {
            log.debug("判断图片位置失败：{}", e.getMessage());
        }
        return false;
    }

    /**
     * 从工作表获取指定单元格的第一个图片数据
     *
     * @param sheet    工作表
     * @param rowNum   行号
     * @param colNum   列号
     * @param workbook 工作簿
     * @return 图片字节数组
     */
    private byte[] getPictureDataFromCell(Sheet sheet, int rowNum, int colNum, Workbook workbook) {
        List<Picture> pictures = getPicturesFromSheet(sheet, rowNum, colNum);
        if (!pictures.isEmpty()) {
            Picture picture = pictures.get(0);
            return getPictureData(picture, workbook);
        }
        return null;
    }

    /**
     * 获取图片的字节数据
     *
     * @param picture  图片对象
     * @param workbook 工作簿
     * @return 图片字节数组
     */
    private byte[] getPictureData(Picture picture, Workbook workbook) {
        try {
            // 直接从 Picture 对象获取数据
            return picture.getPictureData().getData();
        } catch (Exception e) {
            log.warn("获取图片数据失败：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 从 Excel 单元格导入超链接
     *
     * @param cell 单元格对象
     * @param attr Excel 注解
     * @return 链接地址
     */
    private String importLinkFromCell(Cell cell, Excel attr) {
        if (cell == null) {
            return "";
        }

        try {
            // 获取单元格的超链接
            Hyperlink hyperlink = cell.getHyperlink();
            if (hyperlink != null) {
                String address = hyperlink.getAddress();

                // 根据超链接类型处理
                switch (hyperlink.getType()) {
                    case URL:
                        return address;
                    case EMAIL:
                        // 移除 mailto:前缀
                        return address.replaceFirst("^mailto:", "");
                    case FILE:
                        return address;
                    case DOCUMENT:
                        return address;
                    default:
                        return address;
                }
            }

            // 如果没有超链接，但有 linkText 配置，可能是纯文本链接
            if (StringUtils.isNotEmpty(attr.linkText()) && StringUtils.isNotEmpty(cell.getStringCellValue())) {
                // 如果显示文本与 linkText 匹配，尝试从其他地方获取链接
                // 这里可以根据实际需求调整逻辑
            }

            // 返回单元格的实际值（可能是纯文本 URL）
            if (cell.getCellType() == CellType.STRING) {
                String cellValue = cell.getStringCellValue();
                if (StringUtils.isNotEmpty(cellValue) &&
                        (cellValue.startsWith("http://") || cellValue.startsWith("https://") ||
                                cellValue.startsWith("mailto:"))) {
                    return cellValue;
                }
            }
        } catch (Exception e) {
            log.error("导入链接失败：{}", e.getMessage());
        }

        return "";
    }


    /**
     * 在指定单元格位置添加图片（嵌入到单元格内）
     *
     * @param sheet        工作表
     * @param createHelper
     * @param rowNum       起始行号
     * @param colNum       起始列号
     * @param imageData    图片字节数据
     */
    public static void addImageToCell(XSSFSheet sheet, CreationHelper createHelper, int rowNum, int colNum, byte[] imageData, int mediaType) {
        if (imageData == null || imageData.length == 0) {
            return;
        }
        try {
            // 添加图片到工作簿
            int pictureIndex = sheet.getWorkbook().addPicture(imageData, mediaType);
            Drawing<?> drawingPatriarch = sheet.createDrawingPatriarch();
            ClientAnchor anchor = createHelper.createClientAnchor();
            anchor.setCol1(colNum);
            anchor.setCol2(colNum + 1);
            anchor.setRow1(rowNum);
            anchor.setRow2(rowNum + 1);
            drawingPatriarch.createPicture(anchor, pictureIndex);
        } catch (Exception e) {
            throw new RuntimeException("添加图片失败：" + e.getMessage(), e);
        }
    }

    /**
     * 获取示例图片数据
     * 实际使用时可以从文件、数据库或网络获取
     *
     * @return 图片字节数组
     */
    public static byte[] getImageData(String imageUrl) {
        try {
            URL url = new URL(imageUrl);
            try (InputStream is = url.openStream()) {
                return IOUtils.toByteArray(is);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取图片数据失败：" + e.getMessage(), e);
        }
    }


    /**
     * 创建带超链接的单元格
     *
     * @param sheet        工作表
     * @param rowNum       行号
     * @param colNum       列号
     * @param text         显示文本
     * @param address      链接地址
     * @param linkType     链接类型
     * @param style        单元格样式
     * @param createHelper 创建助手
     */
    public static void createHyperlinkCell(XSSFSheet sheet, int rowNum, int colNum, String text,
                                           String address, HyperlinkType linkType, CellStyle style,
                                           CreationHelper createHelper) {
        createHyperlinkCellForSheet(sheet, rowNum, colNum, text, address, linkType, style, createHelper);
    }

    /**
     * 创建带超链接的单元格
     *
     * @param sheet        工作表
     * @param rowNum       行号
     * @param colNum       列号
     * @param text         显示文本
     * @param address      链接地址
     * @param linkType     链接类型
     * @param style        单元格样式
     * @param createHelper 创建助手
     */
    public static void createHyperlinkCellForSheet(Sheet sheet, int rowNum, int colNum, String text,
                                                   String address, HyperlinkType linkType, CellStyle style,
                                                   CreationHelper createHelper) {
        Row row = sheet.createRow(rowNum);
        Cell cell = row.createCell(colNum);
        createHyperlinkCellForSheet(cell, text, address, linkType, style, createHelper);
    }

    public static void createHyperlinkCellForSheet(Cell cell, String text,
                                                   String address, HyperlinkType linkType, CellStyle style,
                                                   CreationHelper createHelper) {
        cell.setCellValue(text);
        Hyperlink hyperlink = createHelper.createHyperlink(linkType);
        hyperlink.setAddress(address);
        cell.setHyperlink(hyperlink);
        cell.setCellStyle(style);
    }


    public static void setBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        ExcelUtils.beanFactory = beanFactory;
    }


    public static void setCellValue(Sheet sheet,int rowIndex, int colIndex, Object value) {

        Row row = sheet.getRow(rowIndex);
        if (row == null) {
            row = sheet.createRow(rowIndex);
        }

        Cell cell = row.getCell(colIndex);
        if (cell == null) {
            cell = row.createCell(colIndex);
        }

        // 根据值的类型设置单元格值
        if (value == null) {
            cell.setBlank();
        } else if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Integer || value instanceof Long) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Double || value instanceof Float) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof BigDecimal) {
            cell.setCellValue(((BigDecimal) value).doubleValue());
        } else {
            cell.setCellValue(value.toString());
        }
    }

}
