package com.hdkj.lt.utils.poi;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;

import lombok.Builder;
import lombok.Data;
import org.apache.commons.codec.Charsets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description
 * @Author Administration
 * @Date 2022/5/25 14:47
 * @Version 1.0
 */
public class POIExcelUtils {

    private POIExcelUtils() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(POIExcelUtils.class);

    public static final String EXCEL_SUFFIX_XLSX = ".xlsx";

    public static final String EXCEL_SUFFIX_XLS = ".xls";

    //小数位位数的格式化 #,##0
    public static final String FORMAT_STR = "0";

    public static final String FORMAT = "%s_%s";

    public static final String CELL_FORMAT = "style_%s";

    public static final String EMPTY_STR = "-";

    /**
     * 导出Excel
     *
     * @param response
     * @param fileName 文件名包含后缀名
     * @param dataList 倒垂数据
     * @param settings 设置
     */
    public static void export(HttpServletResponse response, String fileName, List<Map<String, Object>> dataList, POIExcelSetting settings) {
        try (OutputStream os = response.getOutputStream()) {
            if (!StringUtils.endsWithIgnoreCase(fileName, EXCEL_SUFFIX_XLSX) && !StringUtils.endsWithIgnoreCase(fileName, EXCEL_SUFFIX_XLS)) {
                // 文件名没有后缀时，默认07版本
                fileName = String.format("%s%s", fileName, EXCEL_SUFFIX_XLSX);
            }
            Workbook workbook = doWrite(fileName, dataList, settings);
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding(Charsets.UTF_8.name());
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=%s", URLEncoder.encode(fileName, Charsets.UTF_8.name())));
            workbook.write(os);
            os.flush();
        } catch (IOException ex) {
            throw new RuntimeException(String.format("导出EXCEL文档出现异常：%s", ex.getMessage()));
        }
    }

    public static <T> void buildMultiSheet(List<T> vos, Class<T> clazz, int length, String fileName, HttpServletResponse response){
        List<Map<String, Object>> list = new ArrayList<>();
        int limit = (vos.size() + length - 1) / length;
        Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
            Map<String, Object> map = new HashMap<>();
            ExportParams exportParams = new ExportParams();
            exportParams.setSheetName(String.format("%d-%d", i * length + 1, Math.min((i + 1) * length, vos.size())));
            exportParams.setType(ExcelType.XSSF);
            map.put("title", exportParams);
            map.put("data", vos.stream().skip((long) i * length).limit(length).collect(Collectors.toList()));
            map.put("entity", clazz);
            list.add(map);
        });
        if (list.isEmpty()) {
            Map<String, Object> map = new HashMap<>();
            ExportParams exportParams = new ExportParams();
            exportParams.setType(ExcelType.XSSF);
            map.put("title", exportParams);
            map.put("data", Collections.emptyList());
            map.put("entity", clazz);
            list.add(map);
        }
        Workbook sheets = ExcelExportUtil.exportExcel(list, ExcelType.XSSF);
        EasyPoiUtils.downLoadExcel(fileName, response, sheets);
    }

    /**
     * @param fileName
     * @param dataList
     * @param settings
     * @return
     */
    public static Workbook doWrite(String fileName, List<Map<String, Object>> dataList, POIExcelSetting settings) {
        // 根据文件名来创建不同文档对象
        Workbook workbook = new XSSFWorkbook();
        if (StringUtils.endsWithIgnoreCase(fileName, EXCEL_SUFFIX_XLS)) {
            workbook = new HSSFWorkbook();
        }
        Workbook finalWorkbook = workbook;
        boolean setStyle = !settings.isNoneSetStyle();//是否设置样式，默认设置

        Map<String, CellStyle> cellStyleMap = getBasicCellStyle(finalWorkbook, setStyle, settings);
        //获取 导出表格配置的相关系信息
        String sheetName = Optional.ofNullable(settings.getSheetName()).orElse("");//表格sheet名称
        List<String> headers = settings.getHeaders();//表头列信息
        List<String> columns = settings.getColumns();//表头列对应字段信息
//        List<String> rangeHeader = settings.getRangeHeader();//表头合并时，第一行内容
//        String titleName = settings.getTitleName(); //表格标题内容
        int startRowIndex = Optional.ofNullable(settings.isNoneSetStyle()).map(e -> !e ? 1 : 0).orElse(1);//表格内容开始的下标 第二行开始
        int startLineIndex = Optional.ofNullable(settings.isNoneSetStyle()).map(e -> !e ? 1 : 0).orElse(1);//表格内容开始的下标 第二列开始
        Sheet sheet = finalWorkbook.createSheet(sheetName);
        // 处理表头信息
        if (!CollectionUtils.isEmpty(headers)) {
            startRowIndex = processHeader(settings, sheet, startRowIndex, startLineIndex, headers, cellStyleMap, setStyle);
        }
        //处理导出数据信息
        if (!CollectionUtils.isEmpty(dataList)) {
            processDataList(settings, sheet, startRowIndex, startLineIndex, headers, cellStyleMap, dataList, finalWorkbook);
        } else {
            Row row = sheet.createRow(startRowIndex);
            AtomicInteger lineIndex = new AtomicInteger(startLineIndex);
            AtomicInteger columnIndex = new AtomicInteger(0);//字段下标
            columns.stream().forEachOrdered(key -> {
                Cell contentCell = row.createCell(lineIndex.get());
                String tempStr = String.format("%s_05", key);//默认 下样式
                //创建单元格后 直接给每个单元格先给个基础样式
                if (columnIndex.get() == 0) {
                    tempStr = String.format("%s_04", "style");//内容左下样式
                } else if (columnIndex.get() == headers.size() - 1) {
                    tempStr = String.format("%s_06", key);//内容右下样式
                }
                contentCell.setCellStyle(cellStyleMap.get(tempStr));
                contentCell.setCellValue("");
                lineIndex.getAndIncrement();
                columnIndex.getAndIncrement();
            });
        }
//        setSheetColumnWidth(sheet, headers.size());// 设置宽度
        return finalWorkbook;
    }

    /**
     * 处理表头
     *
     * @param settings
     * @param sheet
     * @param startRowIndex
     * @param startLineIndex
     * @param headers
     * @param cellStyleMap
     * @param setStyle
     */
    private static int processHeader(POIExcelSetting settings, Sheet sheet, int startRowIndex, int startLineIndex, List<String> headers, Map<String, CellStyle> cellStyleMap, boolean setStyle) {
        List<String> rangeHeader = settings.getRangeHeader();//表头合并时，第一行内容
        String titleName = settings.getTitleName(); //表格标题内容
        //有标题时第二行写入标题
        if (StringUtils.isNotEmpty(titleName)) {
            Row row = sheet.createRow(startRowIndex);
            AtomicInteger index = new AtomicInteger(startLineIndex);
            final int rangeIndex = startRowIndex;
            headers.stream().forEachOrdered(e -> {
                Cell cell = row.createCell(index.get());
                if (index.get() == startLineIndex) {//表示0和1
                    cell.setCellValue(titleName);
                    if ("left".equals(settings.titleStyle)) {
                        //设置样式 ：左对齐，加粗，自动换行
                        cell.setCellStyle(cellStyleMap.get(String.format(CELL_FORMAT, "000")));
                        row.setHeightInPoints(settings.getTitleHeight());//设置行高
                    }else {
                        cell.setCellStyle(cellStyleMap.get(String.format(CELL_FORMAT, "00")));
                    }
                }
                index.getAndIncrement();
            });
            //合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列
            int regEndIndex = setStyle ? headers.size() : headers.size() - 1;
            sheet.addMergedRegion(new CellRangeAddress(rangeIndex, rangeIndex, startLineIndex, regEndIndex));
            startRowIndex++;//起始位置加一行
        }
        if (!CollectionUtils.isEmpty(rangeHeader)) {
            startRowIndex = processRangeHeader(settings, sheet, startRowIndex, startLineIndex, headers, cellStyleMap);
        } else {
            Row row = sheet.createRow(startRowIndex);
            AtomicInteger index = new AtomicInteger(startLineIndex);
            AtomicInteger columnIndex = new AtomicInteger(0);//字段下标
            headers.stream().forEachOrdered(e -> {
                Cell cell = row.createCell(index.get());
                String tempStr = "02";//默认表头上样式
                //创建单元格后 直接给每个单元格先给个基础样式
                if (columnIndex.get() == 0) {
                    tempStr = "01";//表头左上样式
                } else if (columnIndex.get() == headers.size() - 1) {
                    tempStr = "03";//表头右上样式
                }
                cell.setCellStyle(cellStyleMap.get(String.format(CELL_FORMAT, tempStr)));
                cell.setCellValue(e);
                index.getAndIncrement();
                columnIndex.getAndIncrement();
            });
            startRowIndex++;//起始位置加一行
        }
        return startRowIndex;
    }

    /**
     * 合并表头
     *
     * @param settings
     * @param sheet
     * @param startRowIndex
     * @param startLineIndex
     * @param headers
     * @param cellStyleMap
     */
    private static int processRangeHeader(POIExcelSetting settings, Sheet sheet, int startRowIndex, int startLineIndex, List<String> headers, Map<String, CellStyle> cellStyleMap) {
        List<String> rangeHeader = settings.getRangeHeader();//表头合并时，第一行内容
        Row rangeRow = sheet.createRow(startRowIndex); //合并行
        Row headerRow = sheet.createRow(startRowIndex + 1); //标题行
        AtomicInteger index = new AtomicInteger(startLineIndex);//列下标
        final int rangeRowIndex = startRowIndex;// 合并开始行下标
        AtomicInteger rangeLineIndex = new AtomicInteger(startLineIndex);// 合并开始列下标
        AtomicInteger columnIndex = new AtomicInteger(0);//字段下标
        headers.stream().forEachOrdered(e -> {
            Cell rangeCell = rangeRow.createCell(index.get());
            Cell headerCell = headerRow.createCell(index.get());
            //创建单元格后 直接给每个单元格先给个基础样式
            String rangeCellStr = "02"; //默认 表头上样式
            String headerCellStr = "12"; //默认 表头中样式
            if (columnIndex.get() == 0) {
                rangeCellStr = "01";//表头左上样式
                headerCellStr = "11";//表头左样式
            } else if (columnIndex.get() == headers.size() - 1) {
                rangeCellStr = "03";//表头右上样式
                headerCellStr = "13";//表头右样式
            }
            rangeCell.setCellStyle(cellStyleMap.get(String.format(CELL_FORMAT, rangeCellStr)));//合并表头样式
            headerCell.setCellStyle(cellStyleMap.get(String.format(CELL_FORMAT, headerCellStr)));//表头样式
            String rangeStr = rangeHeader.get(columnIndex.get()); // 合并的表头信息
            if (StringUtils.equals(e, rangeStr)) { //上下相同合并
                rangeCell.setCellValue(rangeStr);
                //合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列
                sheet.addMergedRegion(new CellRangeAddress(rangeRowIndex, rangeRowIndex + 1, index.get(), index.get()));
                rangeLineIndex.set(index.get());//合并行时，需要更新列合并的起始值
            } else {
                if (columnIndex.get() + 1 == headers.size() && StringUtils.equals(rangeStr, rangeHeader.get(columnIndex.get() - 1))) {
                    // 合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列
                    sheet.addMergedRegion(new CellRangeAddress(rangeRowIndex, rangeRowIndex, rangeLineIndex.get(), index.get()));
                } else {
                    if (columnIndex.get() != 0 && !StringUtils.equals(rangeStr, rangeHeader.get(columnIndex.get() - 1))) {
                        //不等与前一列值，表示正处于下一个合并列的起始点，合并单元格重新记录起始点 （合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列）
                        if (rangeLineIndex.get() + 1 != index.get()) { //起始和截至只有一列差不需要合并
                            sheet.addMergedRegion(new CellRangeAddress(rangeRowIndex, rangeRowIndex, rangeLineIndex.get(), index.get() - 1));
                        }
                        rangeLineIndex.set(index.get());
                        rangeCell.setCellValue(rangeStr);
                    }
                }
                headerCell.setCellValue(e);
            }
            index.getAndIncrement();
            columnIndex.getAndIncrement();
        });
        startRowIndex += 2;//起始位置加两行
        return startRowIndex;
    }

    /**
     * 处理数据
     *
     * @param settings
     * @param sheet
     * @param startRowIndex
     * @param startLineIndex
     * @param headers
     * @param cellStyleMap
     * @param dataList
     * @param finalWorkbook
     */
    private static void processDataList(POIExcelSetting settings, Sheet sheet, int startRowIndex, int startLineIndex, List<String> headers, Map<String, CellStyle> cellStyleMap, List<Map<String, Object>> dataList, Workbook finalWorkbook) {
        List<String> columns = settings.getColumns();//表头列对应字段信息
        List<String> percentList = settings.getPercents() == null ? new ArrayList<>() : settings.getPercents();
        Map<String, Integer> decimalMap = settings.getDecimals() == null ? new HashMap<>() : settings.getDecimals();
        AtomicInteger rowIndex = new AtomicInteger(startRowIndex);//导出数据开始写入行
        int finalStartRowIndex = startRowIndex;
        // 序号
        AtomicInteger rowNum = new AtomicInteger(1);
        dataList.stream().forEachOrdered(m -> {
            Row row = sheet.createRow(rowIndex.get());
            AtomicInteger lineIndex = new AtomicInteger(startLineIndex);
            AtomicInteger columnIndex = new AtomicInteger(0);//字段下标
            boolean lastRow = (rowIndex.get() - finalStartRowIndex == dataList.size() - 1);
            columns.stream().forEachOrdered(key -> {
                Cell contentCell = row.createCell(lineIndex.get());
                String tempStr = String.format(FORMAT, key, (lastRow ? "05" : "09"));//内容 下，其他样式
                //创建单元格后 直接给每个单元格先给个基础样式
                if (columnIndex.get() == 0) {
                    tempStr = String.format(CELL_FORMAT, (lastRow ? "04" : "07"));//内容左下，左样式
                } else if (columnIndex.get() == headers.size() - 1) {
                    tempStr = String.format(FORMAT, key, (lastRow ? "06" : "08"));//内容右下，右样式
                }
                contentCell.setCellStyle(cellStyleMap.get(tempStr));
                // 写入内容
                String dataValue = Optional.ofNullable(m.get(key)).map(String::valueOf).orElse(EMPTY_STR);
                //存在.00的数据需要处理
                if (StringUtils.startsWith(dataValue, ".0")) {
                    dataValue = String.format("0%s", dataValue);
                }
                AtomicReference<String> cellDataValue = new AtomicReference<>(dataValue);
                AtomicBoolean isDecimal = new AtomicBoolean(false);
                AtomicReference<Integer> decimal = new AtomicReference<>(0);
                decimalMap.entrySet().stream().forEachOrdered(d -> {
                    if (StringUtils.equals(key, d.getKey())) {// 需要小数
                        try {// 判断数字数据是否存在问题
                            if (!StringUtils.equals(EMPTY_STR, cellDataValue.get())) {
                                Double.valueOf(cellDataValue.get());
                                decimal.set(d.getValue());
                            }
                            isDecimal.set(true);
                        } catch (Exception ex) {
                            LOGGER.error(String.format("导出数据中字段%s数字值出现异常：%s", key, ex.getMessage()));
                        }
                        return;
                    }
                });
                AtomicBoolean isPercent = new AtomicBoolean(false);
                percentList.stream().forEachOrdered(p -> {
                    if (StringUtils.equals(key, p)) {// 需要添加百分号
                        try {// 判断百分比数据是否存在问题
                            if (!StringUtils.equals(EMPTY_STR, cellDataValue.get())) {
                                if (StringUtils.endsWith(cellDataValue.get(), "%")) {
                                    cellDataValue.set(cellDataValue.get().replace("%", ""));
                                }
                                Double.valueOf(cellDataValue.get());
                            }
                            isPercent.set(true);
                        } catch (Exception ex) {
                            LOGGER.error(String.format("导出数据中字段%s百分比值出现异常：%s", key, ex.getMessage()));
                        }
                        return;
                    }
                });
                if (columnIndex.get() == 0 && "sn".equals(key)) {
                    contentCell.setCellValue(rowNum.get());
                    rowNum.getAndIncrement(); // 追加序号
                } else {
                    if (isDecimal.get() && !StringUtils.equals(EMPTY_STR, cellDataValue.get())) {
                        //处理数字后赋值
                        if (StringUtils.contains(cellDataValue.get(), ".")) {
                            if (decimal.get() < 0) {
                                // 不设置小数位，但是有小数位时保留原数据的小数位数
                                setOriginDecimalStyle(finalWorkbook, tempStr, contentCell, cellDataValue.get(), cellStyleMap);
                            }
                            double doubleValue = Double.valueOf(cellDataValue.get());
                            contentCell.setCellValue(doubleValue);
                        } else {
                            long integerValue = Long.valueOf(cellDataValue.get());
                            contentCell.setCellValue(integerValue);
                        }
                    } else if (isPercent.get() && !StringUtils.equals(EMPTY_STR, cellDataValue.get())) {
                        double doubleValue = Double.valueOf(cellDataValue.get());
                        contentCell.setCellValue(doubleValue / 100);
                    } else {
                        contentCell.setCellValue(dataValue);
                    }
                }
                lineIndex.getAndIncrement();
                columnIndex.getAndIncrement();
            });
            // 防止最后一行内容过长 查过单元格 多一列赋空处理
            Cell lineCell = row.createCell(lineIndex.get());
            lineCell.setCellValue("");
            rowIndex.getAndIncrement();
        });
    }

    /**
     * 获取文档的基本字体和单元格样式集合
     *
     * @param workbook
     * @param setStyle
     */
    public static Map<String, CellStyle> getBasicCellStyle(Workbook workbook, boolean setStyle, POIExcelSetting settings) {
        Map<String, CellStyle> styleMap = new HashMap<>();
        /** 表格样式含义：
         *  000:标题，左对齐，加粗，自动换行
         *  00：标题，01：表头左上，02：表头上，03：表头右上，11：二表头左，12：二表头中，13：二表头右
         *  04：表格左下，05：表格下，06：表格右下，07：表格左，08：表格右，09：表格其他位置 */
        List<String> styleList = Arrays.asList("000","00", "01", "02", "03", "11", "12", "13", "04", "05", "06", "07", "08", "09");
        styleList.stream().forEachOrdered(f -> {
            Font font = workbook.createFont();
            setBasicFont(font, f);
            CellStyle style = workbook.createCellStyle();
            style.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直居中
            style.setWrapText(false);// 自动换行
            if (!StringUtils.equals(f, "00") && setStyle) {
                // 非title且指定需要样式时，需要添加样式
                setBasicCellStyle(style, f);
            }
            style.setFont(font); //设置字体
            if (StringUtils.contains("00,01,02,03,11,12,13,04,07", f)) {// 设置不需要样式时，直接居中
                style.setAlignment(HorizontalAlignment.CENTER);// 水平居中
                styleMap.put(String.format(CELL_FORMAT, f), style);
            } else if (StringUtils.equals(f, "000")){
                //左对齐，加粗，自动换行
                Font fontLeft = workbook.createFont();
                fontLeft.setBold(true);
                style.setWrapText(true);// 自动换行
                style.setAlignment(HorizontalAlignment.LEFT);// 左对齐
                style.setFont(fontLeft);
                styleMap.put(String.format(CELL_FORMAT, f), style);
            } else {
                // 表格样式05,06,08,09需要根据每列生成不同样式
                List<String> columns = settings.getColumns();
                if (StringUtils.contains("08,06", f)) { //只需要最后一列的格式
                    String col = columns.get(columns.size() - 1);
                    String styleKey = String.format(FORMAT, col, f);
                    setCellContentStyle(workbook, settings, styleMap, style, styleKey);
                } else {
                    AtomicInteger colIndex = new AtomicInteger(1);
                    columns.stream().forEachOrdered(col -> {
                        if (columns.size() != colIndex.get() && colIndex.get() != 1) {
                            String styleKey = String.format(FORMAT, col, f);
                            setCellContentStyle(workbook, settings, styleMap, style, styleKey);
                        }
                        colIndex.getAndIncrement();
                    });
                }
            }

        });
        return styleMap;
    }

    /**
     * 设置文档的基本字体样式
     *
     * @param font
     * @param fontType 边框样式 00：title样式，01||02||03||12||12||13：表头样式，其他：表格内容样式
     */
    public static void setBasicFont(Font font, String fontType) {
        boolean bold = false; // 默认不加粗
        short points = 13;  // 默认字体13
        String fontName = "";
        if (StringUtils.equals("00", fontType)) { //title内容
            fontName = "黑体";
            bold = true; // 标题加粗
        } else if (StringUtils.contains("01,02,03,11,12,13", fontType)) { //表格头内容
            fontName = "黑体";
            points = 11;  // 标题字体11
        } else { // 表格内容
            fontName = "宋体";
            points = 11; // 标题字体11
        }
        font.setFontName(fontName);//字体格式
        font.setFontHeightInPoints(points); //字号和字体
        font.setBold(bold); //不加粗
        font.setColor(Font.COLOR_NORMAL); //字体颜色
    }

    /**
     * 设置文档的基本单元格样式
     *
     * @param style
     * @param styleType 边框样式 01：表头左上，02：表头上，03：表头右上，11：二表头左，12：二表头中，13：二表头右
     *                  04：表格左下，05：表格下，06：表格右下，07：表格左，08：表格右，09：表格其他位置
     */
    public static void setBasicCellStyle(CellStyle style, String styleType) {
        BorderStyle topBorder = BorderStyle.DOTTED;
        BorderStyle leftBorder = BorderStyle.DOTTED;
        BorderStyle rightBorder = BorderStyle.DOTTED;
        BorderStyle bottomBorder = BorderStyle.DOTTED;
        switch (styleType) {
            case "01": //左上角边框
                topBorder = BorderStyle.THICK;
                leftBorder = BorderStyle.THICK;
                break;
            case "02": //上边框
                topBorder = BorderStyle.THICK;
                break;
            case "03": //右上角边框
                topBorder = BorderStyle.THICK;
                rightBorder = BorderStyle.THICK;
                break;
            case "04": //左下角边框
                leftBorder = BorderStyle.THICK;
                bottomBorder = BorderStyle.THICK;
                break;
            case "05": //下边框
                bottomBorder = BorderStyle.THICK;
                break;
            case "06": //右下角边框
                rightBorder = BorderStyle.THICK;
                bottomBorder = BorderStyle.THICK;
                break;
            case "07": //左边框
            case "11":
                leftBorder = BorderStyle.THICK;
                break;
            case "08": //右边框
            case "13":
                rightBorder = BorderStyle.THICK;
                break;
            default:
                break;
        }
        style.setBorderTop(topBorder);
        style.setBorderLeft(leftBorder);
        style.setBorderRight(rightBorder);
        style.setBorderBottom(bottomBorder);
    }

    /**
     * 设置表格内容
     *
     * @param workbook
     * @param settings
     * @param styleMap
     * @param style
     * @param styleKey
     */
    public static void setCellContentStyle(Workbook workbook, POIExcelSetting settings, Map<String, CellStyle> styleMap, CellStyle style, String styleKey) {
        CellStyle resStyle = workbook.createCellStyle();
        resStyle.cloneStyleFrom(style);//克隆一个互不影响的单元格样式，深复制
        List<String> percentList = settings.getPercents(); // 百分号
        Map<String, Integer> decimalMap = settings.getDecimals(); // 小数位
        Map<String, List<String>> alignmentMap = settings.getAlignment(); // 对齐
        boolean noneSetStyle = settings.isNoneSetStyle(); //是否不需要样式，true为不需要样式
        String col = styleKey.substring(0, styleKey.lastIndexOf("_"));
        AtomicReference<String> alignType = new AtomicReference<>("01");// 默认居左
        percentList.stream().forEachOrdered(p -> {
            if (StringUtils.equals(p, col)) {// 需要添加百分号
                alignType.set("02");
                //设置百分比样式
                DataFormat df = workbook.createDataFormat();
                resStyle.setDataFormat(df.getFormat("0.00%"));
                return;
            }
        });
        decimalMap.entrySet().stream().forEachOrdered(d -> {
            if (StringUtils.equals(d.getKey(), col)) {// 需要添加百分号
                alignType.set("02");
                if (d.getValue() > 0) {
                    //保留小数位
                    resStyle.setDataFormat(setDecimalFormat(workbook, d.getValue()));
                }
                return;
            }
        });
        if (noneSetStyle) { //指定不需要样式时，居中
            resStyle.setAlignment(HorizontalAlignment.CENTER);
        } else if (StringUtils.equals("02", alignType.get())) {
            resStyle.setAlignment(HorizontalAlignment.RIGHT);
        } else {
            resStyle.setAlignment(HorizontalAlignment.LEFT);
        }
        if (Objects.nonNull(alignmentMap)) {
            alignmentMap.entrySet().stream().forEachOrdered(a -> {
                if (StringUtils.equals("center", a.getKey())) {
                    a.getValue().stream().forEachOrdered(c -> {
                        if (StringUtils.equals(c, col)) {// 需要添加样式
                            resStyle.setAlignment(HorizontalAlignment.CENTER);
                        }
                    });
                } else if (StringUtils.equals("right", a.getKey())) {
                    a.getValue().stream().forEachOrdered(r -> {
                        if (StringUtils.equals(r, col)) {// 需要添加样式
                            resStyle.setAlignment(HorizontalAlignment.RIGHT);
                        }
                    });
                } else {
                    a.getValue().stream().forEachOrdered(l -> {
                        if (StringUtils.equals(l, col)) {// 需要添加样式
                            resStyle.setAlignment(HorizontalAlignment.LEFT);
                        }
                    });
                }
            });
        }
        styleMap.put(styleKey, resStyle);
    }

    /**
     * 设置小数位格式
     *
     * @param workbook
     * @param decimal  小数位数
     */
    private static short setDecimalFormat(Workbook workbook, Integer decimal) {
        DataFormat df = workbook.createDataFormat();
        StringBuffer formatStr = new StringBuffer();
        formatStr.append(FORMAT_STR);
        for (int i = 0; i < decimal; i++) {
            if (i == 0) {
                formatStr.append(".0");
            } else {
                formatStr.append("0");
            }
        }
        return df.getFormat(formatStr.toString());
    }

    /**
     * 设置原始数据小数位样式
     *
     * @param workbook
     * @param styleKey
     * @param contentCell  当前单元格对象
     * @param value        写入数据
     * @param cellStyleMap 表格样式键值对集合
     */
    private static void setOriginDecimalStyle(Workbook workbook, String styleKey, Cell contentCell, String value, Map<String, CellStyle> cellStyleMap) {
        int length = Optional.ofNullable(StringUtils.split(value, ".")).map(e -> e[1].length()).orElse(0);
        if (length > 0) { // 确保小数位大于0时才设置样式
            String key = String.format(FORMAT, styleKey, length);
            if (ObjectUtils.isEmpty(cellStyleMap.get(key))) {
                CellStyle tempCellStyle = workbook.createCellStyle();
                tempCellStyle.cloneStyleFrom(cellStyleMap.get(styleKey));
                tempCellStyle.setDataFormat(setDecimalFormat(workbook, length));
                cellStyleMap.put(key, tempCellStyle);
            }
            contentCell.setCellStyle(cellStyleMap.get(key));
        }
    }

    /**
     * Excel的列宽设置
     *
     * @param sheet      Excel的sheet对象
     * @param columnNums 列的数量
     */
    private static void setSheetColumnWidth(Sheet sheet, int columnNums) {
        for (int i = 0; i < columnNums; i++) {
            sheet.autoSizeColumn(i + 1);//先设置自动列宽
            //double colWidth = Math.ceil(sheet.getColumnWidth(i + 1) + 3000);
            int colWidth = sheet.getColumnWidth(i + 1) + 3000;
            if (colWidth > 10400) {
                colWidth = 10400;
            }
            sheet.setColumnWidth(i + 1, colWidth);
        }
    }

    @Data
    @Builder
    public static class POIExcelSetting {
        private String sheetName; // 文档SheetName页名称
        private String titleName; // 文档标题名称
        private String titleStyle; // 文档标题样式
        private Float titleHeight; // 文档标题行高
        private List<String> rangeHeader;   // 合并的标题内容
        private List<String> headers; // 文档标题内容
        private List<String> columns;  // 文档标题栏对应取值字段
        private List<String> percents;  // 百分号对应的取值字段
        private Map<String, Integer> decimals;  // 小数位 key字段，value保留小数位，如果保留原始位数，value设置为-1
        private boolean noneSetStyle; // 是否不设置设置样式,true为不需要样式
        private Map<String, List<String>> alignment; // 对齐 key对齐方式（left：左对齐；center：居中；right：右对齐），value字段名
    }

}
