package digital.laboratory.platform.inspection.utils.sewagereport;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.Units;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTDLbls;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.List;

/**
* date: 2024/4/26 11:58
* @author: Chen
* @since JDK 1.8
* Description: 封装的一下poi接口，有生成图表的接口， 创建标题的接口， 创建段落的
*/
@Slf4j
public class WordUtils {


    /**
     * 设置模板样式， 比如标题
     * @param document
     * @param templateStylePath
     */
    public static void setDocumentStyle(XWPFDocument document, String templateStylePath) {
        // word整体样式
        // 读取模板文档
        XWPFDocument template = null;
        // 获得模板文档的整体样式
        CTStyles wordStyles = null;
        try {
            template = new XWPFDocument(new FileInputStream(templateStylePath));
            wordStyles = template.getStyle();
        } catch (XmlException e) {
            e.printStackTrace();
            log.error("模板样式读取失败！");
        } catch (IOException e) {
            e.printStackTrace();
            log.error("模板样式读取失败！");
        }
        // 获取新建文档对象的样式
        XWPFStyles newStyles = document.createStyles();
        // 关键行// 修改设置文档样式为静态块中读取到的样式
        newStyles.setStyles(wordStyles);
    }

    /**
     * 设置模板样式， 比如标题
     * @param document
     * @param inputStream
     */
    public static void setDocumentStyle(XWPFDocument document, InputStream inputStream) {
        // word整体样式
        // 读取模板文档
        XWPFDocument template = null;
        // 获得模板文档的整体样式
        CTStyles wordStyles = null;
        try {
//            OPCPackage open = OPCPackage.open(inputStream);
            template = new XWPFDocument(inputStream);
            wordStyles = template.getStyle();
        } catch (XmlException e) {
            e.printStackTrace();
            log.error("模板样式读取失败！");
        } catch (IOException e) {
            e.printStackTrace();
            log.error("模板样式读取失败！");
        }
        // 获取新建文档对象的样式
        XWPFStyles newStyles = document.createStyles();
        // 关键行// 修改设置文档样式为静态块中读取到的样式
        newStyles.setStyles(wordStyles);
    }

    /**
     * 创建文件首页内容
     * @param document
     * @param docTitle
     * @param fontFamily 字体
     * @param breakSize 换行行数
     * @param fontSize 字体大小
     */
    public static void createDocHomePage(XWPFDocument document, String docTitle, String fontFamily,int breakSize, int fontSize) {
        //新建一个标题段落对象（就是一段文字）
        XWPFParagraph titleParagraph = document.createParagraph();
        //样式居中
        titleParagraph.setAlignment(ParagraphAlignment.CENTER);
        //创建文本对象
        XWPFRun titleFun = titleParagraph.createRun();
        //设置标题的名字
        titleFun.setText(docTitle);
        if (StrUtil.isNotBlank(fontFamily)) {
            titleFun.setFontFamily(fontFamily);
        }
        //加粗
        titleFun.setBold(true);
        //设置颜色
        titleFun.setColor("000000");
        //字体大小
        titleFun.setFontSize(fontSize);
        for (int i = 0; i < breakSize; i++) {
            //换行
            titleFun.addBreak();
        }
    }


    /**
     * 创建文件标题
     *
     * @param document 文档
     * @param docTitle 完档标题
     */
    public static void createDocTitle(XWPFDocument document, String docTitle, boolean isBreak) {
        //新建一个标题段落对象（就是一段文字）
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setStyle("1");
        //样式居中
        titleParagraph.setAlignment(ParagraphAlignment.CENTER);
        //创建文本对象
        XWPFRun titleFun = titleParagraph.createRun();
        //设置标题的名字
        titleFun.setText(docTitle);
        //加粗
        titleFun.setBold(true);
        //设置颜色
        titleFun.setColor("000000");
        //字体大小
        titleFun.setFontSize(16);
        if (isBreak) {
            //换行
            titleFun.addBreak();
        }
    }

    /***
     * 创建段落标题 ， 无标题设置
     * @param document 文档
     * @param parTitle 段落标题
     * @param isBreak 是否换行
     */
    public static void createParagraphTitle(XWPFDocument document, String parTitle, boolean isBreak) {
        XWPFParagraph titleParagraph = document.createParagraph();

        setParagraphTitleProperty(parTitle, isBreak, titleParagraph);
    }


    /***
     * 创建段落标题 有标题设置
     * @param document 文档
     * @param parTitle 段落标题
     * @param isBreak 是否换行
     * @param styleLevel 标题等级
     */
    public static void createParagraphTitle(XWPFDocument document, String parTitle, boolean isBreak, Integer styleLevel) {
        XWPFParagraph titleParagraph = document.createParagraph();
        // 设置标题等级
        titleParagraph.setStyle(styleLevel.toString());

        setParagraphTitleProperty(parTitle, isBreak, titleParagraph);
    }

    /**
     * 创建word文档段落, 可以选择设置tab的数量
     *
     * @param document  文档
     * @param align     ParagraphAlignment.LEFT 居左
     * @param paragraph 内容
     * @param isBreak   是否换行
     * @param tabCount 需要tab的数量
     */
    public static void createParagraphCustomTab(XWPFDocument document, ParagraphAlignment align, String paragraph, boolean isBreak, int firstLintIndent, int tabCount) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(align);
        XWPFRun titleFun = titleParagraph.createRun();
        titleFun.setFontSize(12);
        for (int i = 0; i < tabCount; i++) {
            titleFun.addTab();
        }
        titleFun.setFontFamily("宋体");
        titleFun.setText(paragraph);
        titleFun.setColor("000000");
        titleParagraph.setFirstLineIndent(firstLintIndent);
        // 设置段落行距
        setSingleLineSpacing(titleParagraph);
        //换行
        if (isBreak) {
            titleFun.addBreak();
        }
    }

    /**
     * 创建word文档段落
     *
     * @param document  文档
     * @param align     ParagraphAlignment.LEFT 居左
     * @param paragraph 内容
     * @param isBreak   是否换行
     */
    public static void createParagraph(XWPFDocument document, ParagraphAlignment align, String paragraph, boolean isBreak, int firstLintIndent) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(align);
        XWPFRun titleFun = titleParagraph.createRun();
        titleFun.setFontSize(12);

        titleFun.addTab();

        titleFun.setFontFamily("宋体");
        titleFun.setText(paragraph);
        titleFun.setColor("000000");
        titleParagraph.setFirstLineIndent(firstLintIndent);
        // 设置段落行距
        setSingleLineSpacing(titleParagraph);
        //换行
        if (isBreak) {
            titleFun.addBreak();
        }
    }

    /**
     * 创建word文档高亮段落
     *
     * @param document  文档
     * @param align     ParagraphAlignment.LEFT 居左
     * @param paragraph 内容
     * @param isBreak   是否换行
     */
    public static void createHighLightParagraph(XWPFDocument document, ParagraphAlignment align, String paragraph, boolean isBreak, int firstLintIndent) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(align);
        XWPFRun titleFun = titleParagraph.createRun();
        highLight(titleParagraph, titleFun);
        titleFun.setFontSize(12);

        titleFun.addTab();

        titleFun.setFontFamily("宋体");
        titleFun.setText(paragraph);
        titleFun.setColor("000000");
        titleParagraph.setFirstLineIndent(firstLintIndent);
        // 设置段落行距
        setSingleLineSpacing(titleParagraph);
        //换行
        if (isBreak) {
            titleFun.addBreak();
        }
    }

    /**
     * 设置高亮属性
     * @param p
     * @param run
     */
    private static void highLight(XWPFParagraph p, XWPFRun run) {
        CTRPr pRpr;
        if (run.getCTR() != null) {
            pRpr = run.getCTR().getRPr();
            if (pRpr == null) {
                pRpr = run.getCTR().addNewRPr();
            }
        } else {
            pRpr = p.getCTP().addNewR().addNewRPr();
        }
        CTHighlight highlight = pRpr.addNewHighlight();
        highlight.setVal(STHighlightColor.RED);
    }


    /**
     * 设置段落行距
     * @param paragraph
     */
    public static void setSingleLineSpacing(XWPFParagraph paragraph) {
        CTP ctp = paragraph.getCTP();
        CTPPr ppr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
        CTSpacing spacing = ppr.isSetSpacing()? ppr.getSpacing() : ppr.addNewSpacing();
        spacing.setAfter(BigInteger.valueOf(10));
        spacing.setBefore(BigInteger.valueOf(0));
        //注意设置行距类型为 EXACT
        spacing.setLineRule(STLineSpacingRule.EXACT);
        //1磅数是20
        spacing.setLine(BigInteger.valueOf(480));


    }

    /**
     * 创建word文档段落, 一部分加粗加粗， 一部分正常
     *
     * @param document  文档
     * @param align     ParagraphAlignment.LEFT 居左
     * @param paragraphBold 要加粗的内容
     * @param paragraph 不加粗的内容
     * @param isBreak   是否换行
     */
    public static void createParagraphAndBold(XWPFDocument document, ParagraphAlignment align, String paragraphBold, String paragraph, boolean isBreak, int firstLintIndent) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(align);
        XWPFRun titleFun = titleParagraph.createRun();
        /**
         *
         字体大小	磅值
         五号	9pt
         小四	12pt
         四号	14pt
         三号	16pt
         小二	18pt
         二号	22pt
         一号	26pt
         小初	36pt

         */
        titleFun.setFontSize(12); // 小四
        titleFun.addTab();
        titleFun.setFontFamily("宋体");
        titleFun.setBold(true);
        titleFun.setText(paragraphBold);
        titleFun.setColor("000000");

        XWPFRun xwpfRun = titleParagraph.createRun();
        xwpfRun.setFontSize(12); // 小四
//        xwpfRun.addTab();
        xwpfRun.setFontFamily("宋体");
        xwpfRun.setText(paragraph);
        xwpfRun.setColor("000000");

        titleParagraph.setFirstLineIndent(firstLintIndent);
        setSingleLineSpacing(titleParagraph);
        //换行
        if (isBreak) {
            xwpfRun.addBreak();
        }
    }

    /**
     * 创建word文档段落, 两边是不加粗的内容，中间是需要加粗
     *
     * @param document  文档
     * @param align     ParagraphAlignment.LEFT 居左
     * @param paragraphBold 要加粗的内容
     * @param paragraph1 不加粗的内容1
     * @param paragraph2 不加粗的内容2
     * @param isBreak   是否换行
     */
    public static void createParagraphAndMiddleBold(XWPFDocument document, ParagraphAlignment align, String paragraphBold, String paragraph1, String paragraph2,boolean isBreak, int firstLintIndent) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(align);
        titleParagraph.setFirstLineIndent(firstLintIndent);
        // 第一部分 不加粗内容
        XWPFRun xwpfRun1 = titleParagraph.createRun();
        xwpfRun1.addTab();
        xwpfRun1.setFontSize(12); // 小四
        xwpfRun1.setFontFamily("宋体");
        xwpfRun1.setText(paragraph1);
        xwpfRun1.setColor("000000");

        // 中间加粗的内容
        XWPFRun titleFun = titleParagraph.createRun();
        /**
         *
         字体大小	磅值
         五号	9pt
         小四	12pt
         四号	14pt
         三号	16pt
         小二	18pt
         二号	22pt
         一号	26pt
         小初	36pt
         */
        titleFun.setFontSize(12); // 小四
        titleFun.setFontFamily("宋体");
        titleFun.setBold(true);
        titleFun.setText(paragraphBold);
        titleFun.setColor("000000");
        // 第二部分不加粗的内容
        XWPFRun xwpfRun = titleParagraph.createRun();
        xwpfRun.setFontSize(12); // 小四
        xwpfRun.setFontFamily("宋体");
        xwpfRun.setText(paragraph2);
        xwpfRun.setColor("000000");

        setSingleLineSpacing(titleParagraph);
        //换行
        if (isBreak) {
            titleParagraph.createRun().addBreak();
        }
    }

    /**
     * 创建word文档段落, 两边是加粗的内容，中间是不需要加粗
     *
     * @param document  文档
     * @param align     ParagraphAlignment.LEFT 居左
     * @param paragraph 不要加粗的内容
     * @param paragraphBold1 加粗的内容1
     * @param paragraphBold2 加粗的内容2
     * @param isBreak   是否换行
     */
    public static void createParagraphAndBothSidesBold(XWPFDocument document, ParagraphAlignment align, String paragraph, String paragraphBold1, String paragraphBold2,boolean isBreak, int firstLintIndent) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(align);
        titleParagraph.setFirstLineIndent(firstLintIndent);
        // 第一部分 加粗内容
        XWPFRun xwpfRun = titleParagraph.createRun();
        xwpfRun.setFontSize(12); // 小四
        xwpfRun.setFontFamily("宋体");
        xwpfRun.addTab();
        xwpfRun.setBold(true);
        xwpfRun.setText(paragraphBold1);
        xwpfRun.setColor("000000");

        // 中间不加粗的内容
        XWPFRun titleFun = titleParagraph.createRun();
        /**
         *
         字体大小	磅值
         五号	9pt
         小四	12pt
         四号	14pt
         三号	16pt
         小二	18pt
         二号	22pt
         一号	26pt
         小初	36pt
         */
        titleFun.setFontSize(12); // 小四
        titleFun.setFontFamily("宋体");
        titleFun.setText(paragraph);
        titleFun.setColor("000000");
        // 第二部分加粗的内容
        XWPFRun xwpfRun2 = titleParagraph.createRun();
        xwpfRun2.setFontSize(12); // 小四
        xwpfRun2.setFontFamily("宋体");
        xwpfRun2.setBold(true);
        xwpfRun2.setText(paragraphBold2);
        xwpfRun2.setColor("000000");

        setSingleLineSpacing(titleParagraph);
        //换行
        if (isBreak) {
            titleParagraph.createRun().addBreak();
        }
    }

    /**
     * 创建饼图
     *
     * @param document 文档
     * @param from     数据
     * @throws Exception
     */
    public static void createPieChart(XWPFDocument document, PieChartForm from, String content) throws Exception {
        XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);

        // 设置图例位置:上下左右
        XDDFChartLegend legend = chart.getOrAddLegend();
        legend.setPosition(LegendPosition.BOTTOM);
        legend.setOverlay(false);
        chart.setTitleText(from.getTitle());
        chart.setTitleOverlay(false);
        int numOfPoints = from.getXData().size();
        String[] categories = from.getXData().toArray(new String[]{});
        XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories);
        String range = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
        AxisYVal yData = from.getYData();
        XDDFNumericalDataSource<?> source = XDDFDataSourcesFactory.fromArray(yData.getVal(), range);
        XDDFPieChartData pieChart = (XDDFPieChartData) chart.createData(ChartTypes.PIE, null, null);
        pieChart.setVaryColors(true);
        XDDFPieChartData.Series series = (XDDFPieChartData.Series) pieChart.addSeries(categoriesData, source);
        series.setTitle(yData.getTitle(), setTitleInDataSheet(chart, yData.getTitle(), 0));
        // 设置数据标签显示占比
        series.setShowLeaderLines(true);
        CTDLbls dLbls = series.getCTPieSer().getDLbls();
        setCTDlbls(dLbls, false, false,false,false);
        chart.plot(pieChart);
        setChartFontProperty(document, content);
    }

    /**
     * 创建表格
     *
     * @param document 文档
     * @param from     表格数据
     */
    public static void createTable(XWPFDocument document, TableForm from) {
        log.info(">> 开始生成表格");
        XWPFTable table = document.createTable();
        List<TableForm.CellData> headerList = from.getHeaderList();
        //创建表头
        XWPFTableRow headerRow = getTableRow(table, 0);
        for (int i = 0; i < headerList.size(); i++) {
            TableForm.CellData cellData = headerList.get(i);
            XWPFTableCell cell = getTableRowCell(headerRow, i);
            cell.setText(cellData.getVal());
        }
        //表格数据
        List<List<TableForm.CellData>> rowDataList = from.getRowDataList();
        for (int i = 0; i < rowDataList.size(); i++) {
            int rowIndex = i + 1;
            List<TableForm.CellData> cellData = rowDataList.get(i);
            XWPFTableRow row = getTableRow(table, rowIndex);
            for (int x = 0; x < cellData.size(); x++) {
                TableForm.CellData cellD = cellData.get(x);
                XWPFTableCell cell = getTableRowCell(row, x);
                cell.setText(cellD.getVal());
            }
        }
        log.info(">> 生成表格 完成");

    }

    /**
     * 生成图表 柱状图+折线+ 柱状折线组合
     *
     * @param document  文档
     * @param chartForm 数据
     * @param content 图表名称
     * @param isShowLineVal 折线上是否显示数据
     * @throws Exception
     */
    public static void createBarLineCharts(XWPFDocument document, BarLineChartForm chartForm, String content, boolean isShowLineVal) throws Exception {
        if (chartForm == null) {
            throw new IllegalArgumentException("数据为空");
        }
        List<String> xData = chartForm.getXData();
        if (xData == null) {
            throw new IllegalArgumentException("X轴数据为空");
        }
        List<AxisYVal> yBarData = chartForm.getYBarData();
        List<AxisYVal> yLintData = chartForm.getYLineData();
        if (yBarData == null && yLintData == null) {
            throw new IllegalArgumentException("柱状数据、折线数据为空");
        }
        XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);
        // 设置图例位置:上下左右
        XDDFChartLegend legend = chart.getOrAddLegend();
        legend.setPosition(LegendPosition.TOP);
        legend.setOverlay(false);
        // 设置标题
        chart.setTitleText(chartForm.getTitle());
        //标题覆盖
        chart.setTitleOverlay(false);
        int numOfPoints = xData.size();
        //初始化X轴数据
        String[] categories = xData.toArray(new String[]{});
        String cat = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
        XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, cat, 0);
        //初始化X轴
        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        bottomAxis.setTitle(chartForm.getXTitle());
        //初始化Y周
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setTitle(chartForm.getYTitle());
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
        leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
        int barSize = 0;
        if (yBarData != null) {
            log.info(">> 开始生成柱状图 ");
            // 创建柱状图的类型
            barSize = yBarData.size();
            XDDFBarChartData barChart = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
            barChart.setBarDirection(BarDirection.COL);
            for (int i = 0; i < yBarData.size(); i++) {
                setBarProperty(yBarData, chart, numOfPoints, categoriesData, barChart, i);
            }
            if (yBarData.size() > 1) {
                barChart.setBarGrouping(BarGrouping.STACKED);
//                barChart.setOverlap((byte)-100);// 满值叠加柱子才不会错位
//                barChart.setGapWidth(500);
                // 堆叠柱状图，必须设置为100 不然会柱状图会错位
                chart.getCTChart().getPlotArea().getBarChartArray(0).addNewOverlap().setVal((byte) 100);
            }
            chart.plot(barChart);
            log.info(">> 生成柱状图 完毕 ");
        }

        //********************* 折线图 *************************************************
        if (yLintData != null) {
            log.info(">> 开始生成折线图 ");
            XDDFLineChartData lineChart = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
            lineChart.setVaryColors(false);
            for (int i = 0; i < yLintData.size(); i++) {
                int collIndex = i + 1 + barSize;
                String range = chart.formatRange(new CellRangeAddress(1, numOfPoints, collIndex, collIndex));
                AxisYVal yVal = yLintData.get(i);
                Number[] number = yVal.getVal();
                XDDFNumericalDataSource<?> source = XDDFDataSourcesFactory.fromArray(number, range);
                XDDFLineChartData.Series series = (XDDFLineChartData.Series) lineChart.addSeries(categoriesData, source);
                series.setTitle(yVal.getTitle(), setTitleInDataSheet(chart, yVal.getTitle(), collIndex));
                series.setMarkerStyle(MarkerStyle.STAR);
                series.setMarkerSize((short) 6);
                series.setSmooth(Boolean.FALSE);
                series.setShowLeaderLines(true);
                CTDLbls dLbls = series.getCTLineSer().getDLbls();
                // 控制系列名称是否显示
                setCTDlbls(dLbls,false, isShowLineVal, false, false);
            }

            chart.plot(lineChart);
            log.info(">> 生成折线图 完毕");
        }
        setChartFontProperty(document, content);
    }

    /**
     * 生成横向柱状图
     *
     * @param document  word文档
     * @param chartForm 图表数据
     * @throws Exception
     */
    public static void createBarCharts(XWPFDocument document, BarLineChartForm chartForm) throws Exception {
        if (chartForm == null) {
            throw new IllegalArgumentException("数据为空");
        }
        List<String> xData = chartForm.getXData();
        if (xData == null) {
            throw new IllegalArgumentException("X轴数据为空");
        }
        List<AxisYVal> yBarData = chartForm.getYBarData();
        List<AxisYVal> yLintData = chartForm.getYLineData();
        if (yBarData == null && yLintData == null) {
            throw new IllegalArgumentException("柱状数据、折线数据为空");
        }
        XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);
        // 设置图例位置:上下左右
        XDDFChartLegend legend = chart.getOrAddLegend();
        legend.setPosition(LegendPosition.TOP);
        legend.setOverlay(false);
        // 设置标题
        chart.setTitleText(chartForm.getTitle());
        //标题覆盖
        chart.setTitleOverlay(false);
        int numOfPoints = xData.size();
        //初始化X轴数据
        String[] categories = xData.toArray(new String[]{});
        String cat = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
        XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, cat, 0);
        //初始化X轴
        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        bottomAxis.setTitle(chartForm.getXTitle());
        //初始化Y周
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setTitle(chartForm.getYTitle());
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
        leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
        log.info(">> 开始生成柱状图 ");
        // 创建柱状图的类型
        XDDFBarChartData barChart = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
        barChart.setBarDirection(BarDirection.BAR);
        for (int i = 0; i < yBarData.size(); i++) {
            setBarProperty(yBarData, chart, numOfPoints, categoriesData, barChart, i);
            break;
        }
        if (yBarData.size() > 1) {
            barChart.setBarGrouping(BarGrouping.CLUSTERED);
            chart.getCTChart().getPlotArea().getBarChartArray(0).addNewOverlap().setVal((byte) 0);
        }
        chart.plot(barChart);
        log.info(">> 生成柱状图 完毕 ");


    }

    /**
     * 浏览器下载
     *
     * @param document 文档
     * @param fileName 文件名
     * @throws IOException
     */
    public static void save(XWPFDocument document, String fileName) throws IOException {
        ServletOutputStream out = null;
        try {
            if (document == null || fileName == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            if (requestAttributes != null) {
                ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
                HttpServletResponse response = servletRequestAttributes.getResponse();
                response.setContentType("application/octet-stream");
                out = response.getOutputStream();
                String encodedFileName;
                try {
                    encodedFileName = URLEncoder.encode(fileName, "UTF-8");
                } catch (UnsupportedEncodingException var20) {
                    throw new RuntimeException(var20);
                }
                response.setHeader("Content-Disposition", "attachment;fileName=" + encodedFileName);
                document.write(out);
                return;
            }
            log.warn("非web请求，无法操作。");
        } finally {
            if (document != null) {
                try {
                    document.close();
                } catch (Exception var19) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception var18) {
                }
            }
        }
    }

    /**
     * 另存为
     *
     * @param document 文档
     * @param fileName 文件名
     * @param path     文件存储目录
     * @throws IOException
     */
    public static void saveAs(XWPFDocument document, String fileName, String path) throws IOException {
        FileOutputStream out = null;
        try {
            if (document == null || fileName == null || path == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
            File file = new File(path+fileName);
            String saveAsPath = file.getCanonicalPath();
            out = new FileOutputStream(file);
            log.info("开始另存为word文件[" + saveAsPath + "]", "1");
            document.write(out);
            document.close();
            log.info("成功另存为excel文件[" + saveAsPath + "]", "1");
            return;
        } finally {
            if (document != null) {
                try {
                    document.close();
                } catch (Exception var19) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception var18) {
                }
            }
        }
    }

    ////////// 图表的设置

    /**
     * 为图表设置标题
     * @param chart
     * @param title
     * @param column
     * @return
     * @throws Exception
     */
    private static CellReference setTitleInDataSheet(XWPFChart chart, String title, int column) throws Exception {
        XSSFWorkbook workbook = chart.getWorkbook();
        XSSFSheet sheet = workbook.getSheetAt(0);
        XSSFRow row = sheet.getRow(0);
        if (row == null) {
            row = sheet.createRow(0);
        }
        XSSFCell cell = row.getCell(column);
        if (cell == null) {
            cell = row.createCell(column);
        }
        cell.setCellValue(title);
        return new CellReference(sheet.getSheetName(), 0, column, true, true);
    }

    /**
     * 设置柱状图中柱状的属性
     * @param yBarData
     * @param chart
     * @param numOfPoints
     * @param categoriesData
     * @param barChart
     * @param i
     * @throws Exception
     */
    private static void setBarProperty(List<AxisYVal> yBarData, XWPFChart chart, int numOfPoints, XDDFDataSource<String> categoriesData, XDDFBarChartData barChart, int i) throws Exception {
        int collIndex = i + 1;
        String range = chart.formatRange(new CellRangeAddress(1, numOfPoints, collIndex, collIndex));
        AxisYVal yVal = yBarData.get(i);
        Number[] number = yVal.getVal();
        XDDFNumericalDataSource<?> source = XDDFDataSourcesFactory.fromArray(number, range);
        XDDFBarChartData.Series series = (XDDFBarChartData.Series) barChart.addSeries(categoriesData, source);
        series.setTitle(yVal.getTitle(), setTitleInDataSheet(chart, yVal.getTitle(), collIndex));
    }

    /**
     * 设置图表字体属性
     * @param document
     * @param content
     */
    private static void setChartFontProperty(XWPFDocument document, String content) {
        XWPFParagraph paragraph = document.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun xwpfRun = paragraph.createRun();
        xwpfRun.setFontSize(10);
        xwpfRun.setBold(true);
        xwpfRun.setFontFamily("宋体");
        xwpfRun.setText(content);
        xwpfRun.setColor("000000");
    }

    /**
     * 设置图表的属性显示
     * @param dLbls
     * @param showSerName 控制系列名称是否显示
     * @param showVal 控制值是否显示
     * @param showCatName 控制分类名称显示
     * @param showLegendKey 控制图例标识
     */
    private static void setCTDlbls(CTDLbls dLbls, boolean showSerName, boolean showVal, boolean showCatName, boolean showLegendKey) {
        // 控制系列名称是否显示
        if (dLbls.isSetShowSerName()) {
            dLbls.getShowSerName().setVal(showSerName);
        } else {
            dLbls.addNewShowSerName().setVal(showSerName);
        }
        // 控制值是否显示
        if (dLbls.isSetShowVal()) {
            dLbls.getShowVal().setVal(showVal);
        } else {
            dLbls.addNewShowVal().setVal(showVal);
        }
        // 控制分类名称显示
        if (dLbls.isSetShowCatName()) {
            dLbls.getShowCatName().setVal(showCatName);
        } else {
            dLbls.addNewShowCatName().setVal(showCatName);
        }
        // 控制图例标识
        if (dLbls.isSetShowLegendKey()) {
            dLbls.getShowLegendKey().setVal(showLegendKey);
        } else {
            dLbls.addNewShowLegendKey().setVal(showLegendKey);
        }
    }

    ///////////////////////// 段落的设置
    /**
     * 设置标题属性
     * @param parTitle
     * @param isBreak
     * @param titleParagraph
     */
    private static void setParagraphTitleProperty(String parTitle, boolean isBreak, XWPFParagraph titleParagraph) {
        XWPFRun titleFun = titleParagraph.createRun();
        titleFun.setFontFamily("宋体");
        titleParagraph.setAlignment(ParagraphAlignment.LEFT);
        titleFun.setText(parTitle);
        titleFun.setColor("000000");
        titleFun.setFontSize(14);
        titleFun.setBold(true);
        //换行
        if (isBreak) {
            titleFun.addBreak();
        }
    }


    /////////// 表格的设置
    /**
     * 创建单元格
     * @param row
     * @param index
     * @return
     */
    private static XWPFTableCell getTableRowCell(XWPFTableRow row, int index) {
        XWPFTableCell cell = row.getCell(index);
        if (cell != null) {
            return cell;
        }
        return row.createCell();
    }

    /**
     * 创建表格行
     * @param table
     * @param index
     * @return
     */
    private static XWPFTableRow getTableRow(XWPFTable table, int index) {
        XWPFTableRow row = table.getRow(index);
        if (row != null) {
            return row;
        }
        return table.createRow();
    }
}