许鹏程
2023-06-29 cb4df5a1c9cda76e828fa202990df33be0735105
src/main/java/com/product/server/report/service/GroupReportService.java
@@ -1,5 +1,6 @@
package com.product.server.report.service;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -12,6 +13,10 @@
import com.product.server.report.config.CmnConst;
import com.product.server.report.entity.ReportColumn;
import com.product.util.BaseUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -35,107 +40,56 @@
    * @param recordDte       业务数据dte
    * @param totalName       总计名称
    * @param reportConfigMap 报表配置缓存map
    * @param tableStyle
    * @return 获取分组报表的详情
    */
   public JSONObject getReportEntity(DataTableEntity recordDte, String totalName, Map<Integer, List<JSONObject>> reportConfigMap, StringBuilder tableStyle) {
      StringBuilder reportHtml = new StringBuilder(4096);
   public List<List<ReportColumn>>[] getReport(DataTableEntity recordDte, String totalName, Map<Integer, List<JSONObject>> reportConfigMap) {
//      StringBuilder reportHtml = new StringBuilder(4096);
      List<List<ReportColumn>>[] reportColumnList = new List[]{new ArrayList(), new ArrayList(), new ArrayList()};
      // css
//        StringBuilder cssHtml = dataListReportService.getCssHtml();
//      StringBuilder cssHtml = dataListReportService.getCssHtml();
      // 分组表头区和数据区
      StringBuilder groupAndDataAreaHtml = new StringBuilder(1024);
//      StringBuilder groupAndDataAreaHtml = new StringBuilder(1024);
      Map<String, Set<String>> headAndTailTitleDataMap = Maps.newHashMap();
      int totalColCount = reportConfigMap.get(0).size();
      int dataAreaColCount = getGroupAndDataAreaHtmlAndReturnDataAreaColCount(groupAndDataAreaHtml, reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
      //数据区域数据
      List<List<ReportColumn>> dataAreaList = new ArrayList<>();
      int[] number = getGroupAndDataAreaHtmlAndReturnDataAreaColCount(dataAreaList, reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
      int dataAreaColCount = number[0];
      //标题行数
      int titleAreaRow = number[1];
      totalColCount = Math.max(totalColCount, dataAreaColCount);
      reportColumnList[1] = dataAreaList;
      // 头部标题区
      StringBuilder headTitleHtml = null;
      if (!reportConfigMap.get(1).isEmpty()) {
         headTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
         reportColumnList[0] = dataListReportService.getTitleRows(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
      }
      //从数据区域取出标题行放入头部标题区并将标题行从数据区域删除
      if (titleAreaRow > 0) {
         for (int i = 0; i < titleAreaRow; i++) {
            reportColumnList[0].add(dataAreaList.get(0));
            dataAreaList.remove(0);
         }
      }
      // 底部标题区
      StringBuilder tailTitleHtml = null;
      if (!reportConfigMap.get(3).isEmpty()) {
         tailTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(3), totalColCount, headAndTailTitleDataMap, "tail");
         reportColumnList[2] = dataListReportService.getTitleRows(reportConfigMap.get(3), totalColCount, headAndTailTitleDataMap, "tail");
      }
//        reportHtml.append("\n<body>\n<table").append(tableStyle).append(">")
//                .append(cssHtml)
//                .append(headTitleHtml == null ? "" : headTitleHtml)
//                .append(groupAndDataAreaHtml)
//                .append(tailTitleHtml == null ? "" : tailTitleHtml)
//                .append("\n</table>\n</body>");
      JSONObject resultObj = new JSONObject();
      resultObj.put(CmnConst.RETURN_ATTR_RESULT, true);
      resultObj.put(CmnConst.RETURN_ATTR_MESSAGE, "获取报表成功!");
      resultObj.put(CmnConst.RETURN_ATTR_HTML, reportHtml);
      return resultObj;
   }
   /**
    * 报表-解析
    *
    * @param recordDte       业务数据dte
    * @param totalName       总计名称
    * @param reportConfigMap 报表配置缓存map
    * @param tableStyle
    * @return 获取分组报表的详情
    */
   public JSONObject getReport(DataTableEntity recordDte, String totalName, Map<Integer, List<JSONObject>> reportConfigMap, StringBuilder tableStyle) {
      StringBuilder reportHtml = new StringBuilder(4096);
      // css
      StringBuilder cssHtml = dataListReportService.getCssHtml();
      // 分组表头区和数据区
      StringBuilder groupAndDataAreaHtml = new StringBuilder(1024);
      Map<String, Set<String>> headAndTailTitleDataMap = Maps.newHashMap();
      int totalColCount = reportConfigMap.get(0).size();
      int dataAreaColCount = getGroupAndDataAreaHtmlAndReturnDataAreaColCount(groupAndDataAreaHtml, reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
      totalColCount = Math.max(totalColCount, dataAreaColCount);
      // 头部标题区
      StringBuilder headTitleHtml = null;
      if (!reportConfigMap.get(1).isEmpty()) {
         headTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
      }
      // 底部标题区
      StringBuilder tailTitleHtml = null;
      if (!reportConfigMap.get(3).isEmpty()) {
         tailTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(3), totalColCount, headAndTailTitleDataMap, "tail");
      }
      reportHtml.append("\n<body>\n<table").append(tableStyle).append(">")
            .append(cssHtml)
            .append(headTitleHtml == null ? "" : headTitleHtml)
            .append(groupAndDataAreaHtml)
            .append(tailTitleHtml == null ? "" : tailTitleHtml)
            .append("\n</table>\n</body>");
      JSONObject resultObj = new JSONObject();
      resultObj.put(CmnConst.RETURN_ATTR_RESULT, true);
      resultObj.put(CmnConst.RETURN_ATTR_MESSAGE, "获取报表成功!");
      resultObj.put(CmnConst.RETURN_ATTR_HTML, reportHtml);
      return resultObj;
      return reportColumnList;
   }
   /**
    * 报表-解析-放入分组表头区和数据Html,返回列数
    *
    * @param groupAndDataAreaHtml    html容器
    * @param dataAreaList            数据区域数据
    * @param reportConfigMap         报表缓存数据map
    * @param headAndTailTitleDataMap 头部、尾部标题区数据字段map容器
    * @param recordDte               数据源集合
    * @param totalName               总计名称
    * @return 数据区总列数
    */
   private int getGroupAndDataAreaHtmlAndReturnDataAreaColCount(StringBuilder groupAndDataAreaHtml, Map<Integer, List<JSONObject>> reportConfigMap, Map<String, Set<String>> headAndTailTitleDataMap, DataTableEntity recordDte, String totalName) {
   private int[] getGroupAndDataAreaHtmlAndReturnDataAreaColCount(List<List<ReportColumn>> dataAreaList, Map<Integer, List<JSONObject>> reportConfigMap, Map<String, Set<String>> headAndTailTitleDataMap, DataTableEntity recordDte, String totalName) {
      // 分组表头区字段缓存map
      Map<String, JSONObject> groupAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(2));
      // 数据区字段缓存map
@@ -227,120 +181,13 @@
      // 数据标题list
      List<JSONObject> dataTitleList = Lists.newArrayList();
      // 获取标题html
      groupAndDataAreaHtml.append(getDataAreaTitleHtml(dataTitleList, groupAreaFieldRecordObj, dataAreaFieldConfigMap, groupAreaRangeObj, dataAreaCommonStatisticsFieldNameList));
      List<List<ReportColumn>> dataAreaTitleRows = getDataAreaTitleRows(dataTitleList, groupAreaFieldRecordObj, dataAreaFieldConfigMap, groupAreaRangeObj, dataAreaCommonStatisticsFieldNameList);
      dataAreaList.addAll(dataAreaTitleRows);
      // 获取内容html
      groupAndDataAreaHtml.append(getDataAreaDataHtml(dataTitleList, dataAreaFieldRecordObj, statisticsMap, totalName, dataAreaGroupFieldNameList, dataAreaFieldConfigMap));
      List<List<ReportColumn>> dataAreaDataRows = getDataAreaDataRows(dataTitleList, dataAreaFieldRecordObj, statisticsMap, totalName, dataAreaGroupFieldNameList, dataAreaFieldConfigMap);
      dataAreaList.addAll(dataAreaDataRows);
      return totalColCount;
   }
   /**
    * 报表-解析-放入分组表头区和数据,返回列数
    *
    * @param groupAndDataArea        容器
    * @param reportConfigMap         报表缓存数据map
    * @param headAndTailTitleDataMap 头部、尾部标题区数据字段map容器
    * @param recordDte               数据源集合
    * @param totalName               总计名称
    * @return 数据区总列数
    */
   private int getGroupAndDataAreaAndReturnDataAreaColCount(List<List<ReportColumn>> groupAndDataArea, Map<Integer, List<JSONObject>> reportConfigMap, Map<String, Set<String>> headAndTailTitleDataMap, DataTableEntity recordDte, String totalName) {
      // 分组表头区字段缓存map
      Map<String, JSONObject> groupAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(2));
      // 数据区字段缓存map
      Map<String, JSONObject> dataAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(0));
      // 分组表头区分组统计字段名称list
      List<String> groupAreaGroupStatisticsFieldNameList = Lists.newArrayList();
      // 分组表头区分组字段名称list
      List<String> groupAreaGroupFieldNameList = Lists.newArrayList();
      // 分组表头区区域范围obj
      JSONObject groupAreaRangeObj = new JSONObject();
      groupAreaFieldConfigMap.forEach((fieldName, fieldConfigObj) -> {
         groupAreaGroupFieldNameList.add(fieldName);
         if ("1".equals(fieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
            groupAreaGroupStatisticsFieldNameList.add(fieldName);
         }
         if (groupAreaRangeObj.getIntValue(CmnConst.MIN_X) == 0) {
            groupAreaRangeObj.put(CmnConst.MIN_X, fieldConfigObj.getIntValue(CmnConst.ATTR_X));
         } else {
            groupAreaRangeObj.put(CmnConst.MIN_X, Math.min(groupAreaRangeObj.getIntValue(CmnConst.MIN_X), fieldConfigObj.getIntValue(CmnConst.ATTR_X)));
         }
         groupAreaRangeObj.put(CmnConst.MAX_X, Math.max(groupAreaRangeObj.getIntValue(CmnConst.MAX_X), fieldConfigObj.getIntValue(CmnConst.ATTR_X) + Math.max(1, fieldConfigObj.getIntValue(CmnConst.ATTR_COLSPAN)) - 1));
         groupAreaRangeObj.put(CmnConst.MAX_Y, Math.max(groupAreaRangeObj.getIntValue(CmnConst.MAX_Y), fieldConfigObj.getIntValue(CmnConst.ATTR_Y) + Math.max(1, fieldConfigObj.getIntValue(CmnConst.ATTR_ROWSPAN)) - 1));
      });
      // 数据区分组统计字段名称list
      List<String> dataAreaGroupStatisticsFieldNameList = Lists.newArrayList();
      // 数据区分组字段名称list
      List<String> dataAreaGroupFieldNameList = Lists.newArrayList();
      // 数据区特殊统计字段名称list-分组表头区下统计字段
      List<String> dataAreaSpStatisticsFieldNameList = Lists.newArrayList();
      // 数据区普通统计字段名称list-非数据区分组字段,非分组表头区下统计字段
      List<String> dataAreaCommonStatisticsFieldNameList = Lists.newArrayList();
      dataAreaFieldConfigMap.forEach((fieldName, fieldConfigObj) -> {
         if ("1".equals(fieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
            dataAreaGroupFieldNameList.add(fieldName);
            if ("1".equals(fieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
               dataAreaGroupStatisticsFieldNameList.add(fieldName);
            }
         } else {
            if (fieldConfigObj.getIntValue(CmnConst.ATTR_X) < groupAreaRangeObj.getIntValue(CmnConst.MIN_X) || fieldConfigObj.getIntValue(CmnConst.ATTR_X) > groupAreaRangeObj.getIntValue(CmnConst.MAX_X)) {
               dataAreaCommonStatisticsFieldNameList.add(fieldName);
            } else {
               dataAreaSpStatisticsFieldNameList.add(fieldName);
            }
         }
      });
      // 获取分组表头统计字段对应的数据区字段map
      Map<String, List<String>> groupAreaStatisticsField2DataAreaFieldMap = getGroupAreaStatisticsField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap);
      // 获取分组表头区末行分组字段对应数据区字段map
      Map<String, List<String>> groupAreaLastStageField2DataAreaFieldMap = getGroupAreaLastStageField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap, groupAreaRangeObj);
      // 获取指定数据集中包含的数据区字段集合
      Set<String> headAndTailFieldSet = Sets.newHashSet();
      headAndTailFieldSet.addAll(dataListReportService.getDataFields(reportConfigMap.get(1)));
      headAndTailFieldSet.addAll(dataListReportService.getDataFields(reportConfigMap.get(3)));
      // 统计map
      Map<JSONObject, JSONObject> statisticsMap = Maps.newLinkedHashMap();
      // 分组表头区数据obj
      JSONObject groupAreaFieldRecordObj = new JSONObject(Maps.newTreeMap((o1, o2) -> dataListReportService.compare(o1, o2)));
      // 数据区分组字段obj
      JSONObject dataAreaFieldRecordObj = new JSONObject(Maps.newLinkedHashMap());
      FieldSetEntity recordFse;
      for (int i = 0; i < recordDte.getRows(); i++) {
         recordFse = recordDte.getFieldSetEntity(i);
         // 获取头部、尾部标题区数据字段
         dataListReportService.getHeadAndTailTitleDataMap(headAndTailTitleDataMap, headAndTailFieldSet, recordFse);
         // 获取分组表头区数据obj
         getGroupAreaFieldRecordObj(groupAreaFieldRecordObj, groupAreaFieldConfigMap, recordFse, groupAreaStatisticsField2DataAreaFieldMap, groupAreaLastStageField2DataAreaFieldMap);
         // 获取数据区分组字段obj
         getDataAreaGroupFieldRecordObj(dataAreaFieldRecordObj, dataAreaFieldConfigMap, recordFse);
         // 获取数据区特殊字段的统计
         statisticsDataAreaSpField(statisticsMap, dataAreaFieldConfigMap, dataAreaSpStatisticsFieldNameList, recordFse);
         // 获取数据区普通字段的统计
         statisticsDataAreaCommonField(statisticsMap, dataAreaFieldConfigMap, dataAreaCommonStatisticsFieldNameList, recordFse);
      }
      // 获取分组表头区和数据区总列数
      int totalColCount = getTotalColCount(groupAreaFieldRecordObj, dataAreaGroupFieldNameList.size(), dataAreaCommonStatisticsFieldNameList.size());
      // 数据标题list
      List<JSONObject> dataTitleList = Lists.newArrayList();
      // 获取标题html
      groupAndDataArea.addAll(getDataAreaTitle(dataTitleList, groupAreaFieldRecordObj, dataAreaFieldConfigMap, groupAreaRangeObj, dataAreaCommonStatisticsFieldNameList));
      // 获取内容html
//      groupAndDataAreaHtml.append(getDataAreaDataHtml(dataTitleList, dataAreaFieldRecordObj, statisticsMap, totalName, dataAreaGroupFieldNameList, dataAreaFieldConfigMap));
      return totalColCount;
      return new int[]{totalColCount, dataAreaTitleRows.size()};
   }
   /**
@@ -656,94 +503,6 @@
   }
   /**
    * 获取标题
    *
    * @param dataTitleList                         数据标题list
    * @param groupAreaFieldRecordObj               分组表头区字段记录obj
    * @param dataAreaFieldConfigMap                数据区字段缓存map
    * @param groupAreaRangeObj                     分组表头去范围obj
    * @param dataAreaCommonStatisticsFieldNameList 数据区普通统计字段名称list
    * @return 数据标题html
    */
   private List<List<ReportColumn>> getDataAreaTitle(List<JSONObject> dataTitleList, JSONObject groupAreaFieldRecordObj, Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject groupAreaRangeObj, List<String> dataAreaCommonStatisticsFieldNameList) {
      int maxRow = groupAreaRangeObj.getIntValue(CmnConst.MAX_Y);
      List<List<JSONObject>> groupAreaRangeTitleList = Lists.newArrayList();
      for (int i = 0; i <= maxRow; i++) {
         groupAreaRangeTitleList.add(Lists.newArrayList());
      }
      getGroupAreaRangeTitleList(dataAreaFieldConfigMap, groupAreaFieldRecordObj, groupAreaRangeTitleList, new JSONObject(), 0, maxRow, null);
      List<JSONObject> beforeGroupAreaTitleList = Lists.newArrayList();
      List<JSONObject> afterGroupAreaTitleList = Lists.newArrayList();
      String dataAreaFieldName;
      JSONObject dataAreaFieldConfigObj;
      JSONObject titleObj;
      for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
         dataAreaFieldName = entry.getKey();
         dataAreaFieldConfigObj = entry.getValue();
         titleObj = new JSONObject();
//            titleObj.put(CmnConst.ATTR_FIELD_INFO, getFieldInfo(dataAreaFieldConfigObj));
         titleObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
         if (dataAreaCommonStatisticsFieldNameList.contains(dataAreaFieldName)) {
            titleObj.put(CmnConst.ATTR_SHOW_NAME, dataListReportService.getStatisticsDesc(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS), dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME)));
         } else {
            titleObj.put(CmnConst.ATTR_SHOW_NAME, dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME));
         }
         titleObj.put(CmnConst.ATTR_ROWSPAN, maxRow + 1);
         titleObj.put(CmnConst.ATTR_COLSPAN, 1);
         if (dataAreaFieldConfigObj.getIntValue(CmnConst.ATTR_X) < groupAreaRangeObj.getIntValue(CmnConst.MIN_X)) {
            beforeGroupAreaTitleList.add(titleObj);
         }
         if (dataAreaFieldConfigObj.getIntValue(CmnConst.ATTR_X) > groupAreaRangeObj.getIntValue(CmnConst.MAX_X)) {
            if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
               beforeGroupAreaTitleList.add(titleObj);
            } else {
               afterGroupAreaTitleList.add(titleObj);
            }
         }
      }
      dataTitleList.addAll(groupAreaRangeTitleList.get(maxRow));
      dataTitleList.addAll(afterGroupAreaTitleList);
      List<JSONObject> firstList = Lists.newArrayList();
      firstList.addAll(beforeGroupAreaTitleList);
      firstList.addAll(groupAreaRangeTitleList.get(0));
      firstList.addAll(afterGroupAreaTitleList);
      groupAreaRangeTitleList.set(0, firstList);
//      StringBuilder html = new StringBuilder(1024);
      int rowspan;
      int colspan;
      String value;
      List<List<ReportColumn>> rowColumn = new ArrayList<>();
      for (List<JSONObject> rowList : groupAreaRangeTitleList) {
         List<ReportColumn> columns = new ArrayList<>();
//         html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_TITLE).append("\">\n    ");
         for (JSONObject dataTitleObj : rowList) {
            rowspan = Math.max(1, dataTitleObj.getIntValue(CmnConst.ATTR_ROWSPAN));
            colspan = Math.max(1, dataTitleObj.getIntValue(CmnConst.ATTR_COLSPAN));
            value = dataTitleObj.getString(CmnConst.ATTR_SHOW_NAME);
            if (!StringUtils.isEmpty(dataTitleObj.getString(CmnConst.ATTR_FIELD_INFO))) {
               dataAreaFieldName = dataListReportService.fieldInfo2FieldName(dataTitleObj.getString(CmnConst.ATTR_FIELD_INFO));
               value = dataListReportService.dealTdWidth(dataAreaFieldConfigMap.get(dataAreaFieldName), value);
            }
            ReportColumn column = new ReportColumn();
            column.setRowspan(rowspan);
            column.setColspan(colspan);
            column.setContent(value);
            columns.add(column);
//            html.append("<td rowspan=\"").append(rowspan).append("\" colspan=\"").append(colspan).append("\">").append(value == null ? "" : value).append("</td>");
         }
         rowColumn.add(columns);
//         html.append("\n</tr>");
      }
      return rowColumn;
   }
   /**
    * 获取标题html
    *
    * @param dataTitleList                         数据标题list
@@ -753,7 +512,8 @@
    * @param dataAreaCommonStatisticsFieldNameList 数据区普通统计字段名称list
    * @return 数据标题html
    */
   private StringBuilder getDataAreaTitleHtml(List<JSONObject> dataTitleList, JSONObject groupAreaFieldRecordObj, Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject groupAreaRangeObj, List<String> dataAreaCommonStatisticsFieldNameList) {
   private List<List<ReportColumn>> getDataAreaTitleRows(List<JSONObject> dataTitleList, JSONObject groupAreaFieldRecordObj, Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject groupAreaRangeObj, List<String> dataAreaCommonStatisticsFieldNameList) {
      List<List<ReportColumn>> reportColumnList = Lists.newArrayList();
      int maxRow = groupAreaRangeObj.getIntValue(CmnConst.MAX_Y);
      List<List<JSONObject>> groupAreaRangeTitleList = Lists.newArrayList();
      for (int i = 0; i <= maxRow; i++) {
@@ -807,20 +567,27 @@
      int colspan;
      String value;
      for (List<JSONObject> rowList : groupAreaRangeTitleList) {
         html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_TITLE).append("\">\n    ");
         List<ReportColumn> reportColumns = new ArrayList<>();
//         html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_TITLE).append("\">\n    ");
         for (JSONObject dataTitleObj : rowList) {
            rowspan = Math.max(1, dataTitleObj.getIntValue(CmnConst.ATTR_ROWSPAN));
            colspan = Math.max(1, dataTitleObj.getIntValue(CmnConst.ATTR_COLSPAN));
            value = dataTitleObj.getString(CmnConst.ATTR_SHOW_NAME);
            ReportColumn column = new ReportColumn(colspan, rowspan);
            if (!StringUtils.isEmpty(dataTitleObj.getString(CmnConst.ATTR_FIELD_INFO))) {
               dataAreaFieldName = dataListReportService.fieldInfo2FieldName(dataTitleObj.getString(CmnConst.ATTR_FIELD_INFO));
               value = dataListReportService.dealTdWidth(dataAreaFieldConfigMap.get(dataAreaFieldName), value);
               JSONObject dataAreaFieldConfigObjTemp = dataAreaFieldConfigMap.get(dataAreaFieldName);
               String width = dataAreaFieldConfigObjTemp == null ? null : dataAreaFieldConfigObjTemp.getString(CmnConst.ATTR_WIDTH);
               if (!StringUtils.isEmpty(width) && NumberUtil.isNumber(width)) {
                  column.setColumnWidth(NumberUtil.parseInt(width));
               }
            }
            html.append("<td rowspan=\"").append(rowspan).append("\" colspan=\"").append(colspan).append("\">").append(value == null ? "" : value).append("</td>");
            column.setContent(value);
            reportColumns.add(column);
         }
         html.append("\n</tr>");
         reportColumnList.add(reportColumns);
      }
      return html;
      return reportColumnList;
   }
   /**
@@ -926,7 +693,7 @@
    * @param dataAreaFieldConfigMap     数据区字段缓存map
    * @return 业务数据内容html
    */
   private StringBuilder getDataAreaDataHtml(List<JSONObject> dataTitleList, JSONObject dataAreaFieldRecordObj, Map<JSONObject, JSONObject> statisticsMap, String totalName, List<String> dataAreaGroupFieldNameList, Map<String, JSONObject> dataAreaFieldConfigMap) {
   private List<List<ReportColumn>> getDataAreaDataRows(List<JSONObject> dataTitleList, JSONObject dataAreaFieldRecordObj, Map<JSONObject, JSONObject> statisticsMap, String totalName, List<String> dataAreaGroupFieldNameList, Map<String, JSONObject> dataAreaFieldConfigMap) {
      List<List<JSONObject>> dataAreaGroupFieldDataList = Lists.newArrayList();
      getDataAreaGroupFieldDataList(dataAreaFieldRecordObj, dataAreaGroupFieldDataList, new JSONObject(), 1, 1, dataAreaGroupFieldNameList.size(), null);
@@ -1028,125 +795,34 @@
      if (!StringUtils.isEmpty(totalName)) {
         html.append(getTotalStatisticsHtml(statisticsMap, dataTitleList, dataAreaFieldConfigMap, dataAreaGroupFieldNameList.size(), getSubCnt(dataAreaFieldRecordObj), totalName));
      }
      return html;
   }
   /**
    * 获取内容html
    *
    * @param dataTitleList              数据标题list
    * @param dataAreaFieldRecordObj     数据区字段记录obj
    * @param statisticsMap              统计map
    * @param totalName                  总计名称,非空-需要总计
    * @param dataAreaGroupFieldNameList 数据区分组字段名称list
    * @param dataAreaFieldConfigMap     数据区字段缓存map
    * @return 业务数据内容html
    */
   private List<List<ReportColumn>> getDataAreaData(List<JSONObject> dataTitleList, JSONObject dataAreaFieldRecordObj, Map<JSONObject, JSONObject> statisticsMap, String totalName, List<String> dataAreaGroupFieldNameList, Map<String, JSONObject> dataAreaFieldConfigMap) {
      List<List<JSONObject>> dataAreaGroupFieldDataList = Lists.newArrayList();
      getDataAreaGroupFieldDataList(dataAreaFieldRecordObj, dataAreaGroupFieldDataList, new JSONObject(), 1, 1, dataAreaGroupFieldNameList.size(), null);
      List<JSONObject> groupTitleList = Lists.newArrayList();
      JSONObject obj;
      for (List<JSONObject> rowList : dataAreaGroupFieldDataList) {
         obj = dataListReportService.extendJSONObject(null, rowList.get(rowList.size() - 1), Arrays.asList(CmnConst.ATTR_ROWSPAN, CmnConst.ATTR_COLSPAN, CmnConst.ATTR_SHOW_NAME, CmnConst.ATTR_IS_STATISTICS_FIELD), -1);
         groupTitleList.add(obj);
      }
      StringBuilder html = new StringBuilder(1024);
      JSONObject dataAreaGroupFieldObj;
      int rowspan;
      int colspan;
      String value;
      JSONObject groupTitleObj;
      JSONObject keyObj;
      JSONObject valueObj;
      String statisticsType;
      JSONObject dataAreaFieldConfigObj;
      int groupAreaSubCnt;
      int dataAreaSubCnt;
      List<JSONObject> curRowList;
      int index;
      for (int i = 0; i < dataAreaGroupFieldDataList.size(); i++) {
         index = 0;
         curRowList = dataAreaGroupFieldDataList.get(i);
         if (!curRowList.isEmpty()) {
            for (int j = 0; j < curRowList.size(); j++) {
               dataAreaGroupFieldObj = curRowList.get(j);
               if (dataAreaGroupFieldObj.isEmpty()) {
                  continue;
               }
               if (index == 0) {
                  if ("1".equals(dataAreaGroupFieldObj.getString(CmnConst.ATTR_IS_STATISTICS_FIELD))) {
                     html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
                  } else {
                     html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
                  }
               }
               rowspan = Math.max(1, dataAreaGroupFieldObj.getIntValue(CmnConst.ATTR_ROWSPAN));
               colspan = Math.max(1, dataAreaGroupFieldObj.getIntValue(CmnConst.ATTR_COLSPAN));
               value = dataAreaGroupFieldObj.getString(CmnConst.ATTR_SHOW_NAME);
               html.append("<td rowspan=\"").append(rowspan).append("\" colspan=\"").append(colspan).append("\">").append(value == null ? "" : value).append("</td>");
               index++;
      List<List<ReportColumn>> reportColumnList = new ArrayList<>();
      //解析html
      //在html前面拼接一个table标签和tbody,否则Jsoup解析会报错
      html.insert(0, "<table><tbody>");
      html.append("</tbody></table>");
      Document doc = Jsoup.parse(html.toString());
      Elements trs = doc.select("tr");
      Elements tds;
      List<ReportColumn> reportColumn;
      ReportColumn column;
      for (Element tr : trs) {
         tds = tr.select("td");
         reportColumn = new ArrayList<>();
         for (Element td : tds) {
            column = new ReportColumn();
            column.setColspan(Integer.parseInt(BaseUtil.ifNull(td.attr("colspan"), "1")));
            column.setRowspan(Integer.parseInt(BaseUtil.ifNull(td.attr("rowspan"), "1")));
            Set<String> classNames = td.classNames();
            if (classNames.contains(CmnConst.CLASS_TD_CAN_TURN)) {
               column.setPenetrate(true);
               column.setPenetrateProperty(td.attr("router"));
            }
         } else {
            html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
            column.setContent(td.text());
            reportColumn.add(column);
         }
         groupTitleObj = groupTitleList.get(i);
         for (JSONObject dataTitleObj : dataTitleList) {
            keyObj = dataListReportService.extendJSONObject((JSONObject) groupTitleObj.clone(), dataTitleObj, Arrays.asList(CmnConst.ATTR_ROWSPAN, CmnConst.ATTR_COLSPAN, CmnConst.ATTR_SHOW_NAME, CmnConst.ATTR_IS_STATISTICS_FIELD, CmnConst.ATTR_REAL_VALUE), -1);
            dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataListReportService.fieldInfo2FieldName(dataTitleObj.getString(CmnConst.ATTR_FIELD_INFO)));
            if (dataAreaFieldConfigObj != null) {
               groupAreaSubCnt = keyObj.getIntValue(CmnConst.ATTR_GROUP_AREA_SUB_CNT);
               dataAreaSubCnt = keyObj.getIntValue(CmnConst.ATTR_DATA_AREA_SUB_CNT);
               keyObj.remove(CmnConst.ATTR_GROUP_AREA_SUB_CNT);
               keyObj.remove(CmnConst.ATTR_DATA_AREA_SUB_CNT);
               keyObj.remove(CmnConst.ATTR_REAL_VALUE);
               statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
               valueObj = statisticsMap.get(keyObj);
               if (valueObj == null || valueObj.isEmpty()) {
                  value = getFinalValue(keyObj, dataAreaFieldConfigObj, statisticsMap);
               } else {
                  if (CmnConst.ATTR_STATISTICS_AVG.equals(statisticsType) && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_AVG_TYPE))) {
                     if (dataAreaSubCnt == 0 && groupAreaSubCnt == 0) {
                        value = valueObj.getString(statisticsType);
                     } else {
                        value = getTwoTypesGroupAvgStatisticsValue(dataAreaSubCnt, groupAreaSubCnt, valueObj, dataAreaFieldConfigObj, statisticsType);
                     }
                  } else {
                     value = valueObj.getString(statisticsType);
                  }
                  valueObj.put(CmnConst.ATTR_FINAL_VALUE, value);
               }
            } else {
               value = "";
            }
            html.append("<td");
            if (!StringUtils.isEmpty(value)) {
               // class
               if (!StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL))) {
                  dataListReportService.extendJSONObject(keyObj, dataAreaFieldConfigObj, Collections.singletonList(CmnConst.ATTR_URL));
                  html.append(" class=\"").append(CmnConst.CLASS_TD_CAN_TURN).append("\"");
               }
               // 字段属性
               dataListReportService.getTdAttrObj(keyObj, groupTitleObj);
               dataListReportService.getTdAttrObj(keyObj, dataTitleObj);
               html.append(dataListReportService.getTdAttrByObj(keyObj));
               // 格式
               value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            }
            html.append(">").append(value == null ? "" : value).append("</td>");
         }
         html.append("\n</tr>");
         reportColumnList.add(reportColumn);
      }
      // 总计
      if (!StringUtils.isEmpty(totalName)) {
         html.append(getTotalStatisticsHtml(statisticsMap, dataTitleList, dataAreaFieldConfigMap, dataAreaGroupFieldNameList.size(), getSubCnt(dataAreaFieldRecordObj), totalName));
      }
      return null;
      return reportColumnList;
   }
   /**