许鹏程
2023-06-29 cb4df5a1c9cda76e828fa202990df33be0735105
src/main/java/com/product/server/report/service/CommonReportService.java
@@ -1,6 +1,5 @@
package com.product.server.report.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
@@ -14,9 +13,7 @@
import com.product.core.spring.context.SpringMVCContextHolder;
import com.product.server.report.config.CmnConst;
import com.product.server.report.entity.ReportColumn;
import com.product.server.report.entity.ReportEntity;
import com.product.util.BaseUtil;
import com.product.util.SystemParamReplace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -34,92 +31,49 @@
   @Autowired
   private DataListReportService dataListReportService;
   /**
    * 报表-解析
    *
    * @param recordDte           业务数据dte
    * @param totalStatisticsFlag 是否合计标识
    * @param reportConfigMap     报表配置缓存map
    * @return
    */
   public ReportEntity getReportEntity(DataTableEntity recordDte, String totalName, Map<Integer, List<JSONObject>> reportConfigMap) {
      ReportEntity report = null;
      Map<String, Set<String>> headAndTailTitleDataMap = Maps.newHashMap();
      // 数据区
      report = getDataArea(reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
      int totalColCount = reportConfigMap.get(0).size();
      // 头部标题区
      List<List<ReportColumn>> headTitleRows = null;
      if (!reportConfigMap.get(1).isEmpty()) {
         headTitleRows = dataListReportService.getTitle(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
      }
      // 底部标题区
      List<List<ReportColumn>> tailTitleRows = null;
      if (!reportConfigMap.get(3).isEmpty()) {
         tailTitleRows = dataListReportService.getTitle(reportConfigMap.get(3), totalColCount, headAndTailTitleDataMap, "tail");
      }
      if (!CollectionUtil.isEmpty(headTitleRows)) {
         List<List<ReportColumn>> reportHeader = report.getReportHeader();
         if (reportHeader != null) {
            headTitleRows.addAll(reportHeader);
         }
         report.setReportHeader(headTitleRows);
      }
      if (!CollectionUtil.isEmpty(tailTitleRows)) {
         report.setReportTail(tailTitleRows);
      }
      return report;
   }
   /**
    * 报表-解析
    *
    * @param recordDte           业务数据dte
    * @param totalStatisticsFlag 是否合计标识
    * @param reportConfigMap     报表配置缓存map
    * @param tableStyle
    * @param recordDte            业务数据dte
    * @param totalName            总计名称
    * @param reportConfigMap      报表配置缓存map
    * @param unpivotInfoContainer 行列转换信息
    * @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();
   public List<List<ReportColumn>>[] getReport(DataTableEntity recordDte, String totalName, Map<Integer, List<JSONObject>> reportConfigMap, Collection<String> unpivotInfoContainer) {
      List<List<ReportColumn>>[] reportColumnList = new List[]{new ArrayList(), new ArrayList(), new ArrayList()};
      // 数据区
      Map<String, Set<String>> headAndTailTitleDataMap = Maps.newHashMap();
      String dataAreaHtml = getDataAreaHtml(reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
      int totalColCount = reportConfigMap.get(0).size();
      List<List<ReportColumn>> reportRows = getDataArea(reportConfigMap, headAndTailTitleDataMap, recordDte, totalName, unpivotInfoContainer);
      int totalColCount = reportConfigMap.get(0).size() + unpivotInfoContainer.size() - 1;
      // 头部标题区
      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"));
      }
      //如果reportRows 行数大于0将第一行的数据放入头部标题区
      if (reportRows.size() >= 0) {
         reportColumnList[0].add(reportRows.get(0));
         reportRows.remove(0);
      }
      reportColumnList[1] = reportRows;
      // 底部标题区
      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 class=\"report_main\"").append(tableStyle).append(">")
            .append(cssHtml)
            .append(headTitleHtml == null ? "" : headTitleHtml)
            .append(dataAreaHtml)
            .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;
//      reportHtml.append("\n<body>\n<table class=\"report_main\"").append(tableStyle).append(">")
//            .append(headTitleHtml == null ? "" : headTitleHtml)
//            .append(dataAreaHtml)
//            .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 reportColumnList;
   }
   /**
@@ -131,7 +85,8 @@
    * @param totalName               总计名称,非空-需要总计
    * @return
    */
   private String getDataAreaHtml(Map<Integer, List<JSONObject>> reportConfigMap, Map<String, Set<String>> headAndTailTitleDataMap, DataTableEntity recordDte, String totalName) {
   private List<List<ReportColumn>> getDataArea(Map<Integer, List<JSONObject>> reportConfigMap, Map<String, Set<String>> headAndTailTitleDataMap, DataTableEntity recordDte, String totalName, Collection<String> unpivotInfoContainer) {
      List<List<ReportColumn>> reportColumnList = Lists.newArrayList();
      // 数据区字段缓存map
      Map<String, JSONObject> dataAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(0));
      // 获取指定数据集中包含的数据区字段集合
@@ -141,358 +96,42 @@
      // 统计map
      Map<JSONObject, JSONObject> statisticsMap = Maps.newLinkedHashMap();
      StringBuilder dataAreaHtml = new StringBuilder(1024);
      // 标题
      dataAreaHtml.append(getDataAreaTitleHtml(dataAreaFieldConfigMap));
      // 内容
      dataAreaHtml.append(getDataAreaDataHtml(dataAreaFieldConfigMap, recordDte, headAndTailTitleDataMap, headAndTailFieldSet, statisticsMap, totalName));
      return dataAreaHtml.toString();
   }
   private ReportEntity getDataArea(Map<Integer, List<JSONObject>> reportConfigMap, Map<String, Set<String>> headAndTailTitleDataMap, DataTableEntity recordDte, String totalName) {
      // 数据区字段缓存map
      Map<String, JSONObject> dataAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(0));
      // 获取指定数据集中包含的数据区字段集合
      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();
      List<ReportColumn> reportHeader = getReportHeader(dataAreaFieldConfigMap);
      List<List<ReportColumn>> reportData = getReportData(dataAreaFieldConfigMap, recordDte, headAndTailTitleDataMap, headAndTailFieldSet, statisticsMap, totalName);
      ReportEntity report = new ReportEntity();
      report.addReportHeader(reportHeader);
      report.setReportData(reportData);
      return report;
   }
   private List<ReportColumn> getReportHeader(Map<String, JSONObject> dataAreaFieldConfigMap) {
      if (CollectionUtil.isEmpty(dataAreaFieldConfigMap)) {
         return null;
      // 将行列转换的信息添加到数据区字段缓存map里面
      Map<String, JSONObject> newDataAreaFieldConfigMap = Maps.newLinkedHashMap();
      int x = 0;
      if (!unpivotInfoContainer.isEmpty()) {
         for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            String fieldName = entry.getKey();
            JSONObject fieldConfigObj = entry.getValue();
            if (fieldConfigObj.getIntValue(CmnConst.IS_UNPIVOT) == 1) {
               x = fieldConfigObj.getIntValue(CmnConst.ATTR_X);
               fieldConfigObj.put(CmnConst.IS_UNPIVOT, 0);
               fieldConfigObj.put(CmnConst.ATTR_IS_CUSTOM_FIELD, 0);
               for (String unpivotField : unpivotInfoContainer) {
                  JSONObject newFieldConfigObj = new JSONObject();
                  newFieldConfigObj.putAll(fieldConfigObj);
                  newFieldConfigObj.put(CmnConst.ATTR_X, ++x);
                  newFieldConfigObj.put(CmnConst.ATTR_SHOW_NAME, newFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME).replace("{#unpivot_name#}", unpivotField));
                  newDataAreaFieldConfigMap.put(unpivotField, newFieldConfigObj);
               }
            } else {
               if (x > fieldConfigObj.getIntValue(CmnConst.ATTR_X)) {
                  fieldConfigObj.put(CmnConst.ATTR_X, ++x);
               }
               newDataAreaFieldConfigMap.put(fieldName, fieldConfigObj);
            }
         }
      } else {
         newDataAreaFieldConfigMap = dataAreaFieldConfigMap;
      }
      List<ReportColumn> reportColumnList = new ArrayList<>();
      dataAreaFieldConfigMap.forEach((k, v) -> {
         ReportColumn column = new ReportColumn();
         column.setContent(v.getString(CmnConst.ATTR_SHOW_NAME));
         column.setColumnWidth(NumberUtil.isNumber(v.getString(CmnConst.ATTR_WIDTH)) ? NumberUtil.parseInt(v.getString(CmnConst.ATTR_WIDTH)) : 0);
         reportColumnList.add(column);
      });
      // 标题
      reportColumnList.add(getDataAreaTitle(newDataAreaFieldConfigMap));
      // 内容
      List<List<ReportColumn>> dataAreaData = getDataAreaData(newDataAreaFieldConfigMap, recordDte, headAndTailTitleDataMap, headAndTailFieldSet, statisticsMap, totalName);
      reportColumnList.addAll(dataAreaData);
      return reportColumnList;
   }
   private List<List<ReportColumn>> getReportData(Map<String, JSONObject> dataAreaFieldConfigMap, DataTableEntity recordDte, Map<String, Set<String>> headAndTailTitleDataMap, Set<String> headAndTailFieldSet, Map<JSONObject, JSONObject> statisticsMap, String totalName) {
      List<List<ReportColumn>> reportData = new ArrayList<>();
      // 数据区分组统计字段名称list
      List<String> dataAreaGroupStatisticsFieldNameList = Lists.newArrayList();
      // 数据区分组字段名称list
      List<String> dataAreaGroupFieldNameList = Lists.newArrayList();
      dataAreaFieldConfigMap.forEach((dataAreaFieldName, dataAreaFieldConfigObj) -> {
         if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
            dataAreaGroupFieldNameList.add(dataAreaFieldName);
            if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
               dataAreaGroupStatisticsFieldNameList.add(dataAreaFieldName);
            }
         }
      });
      FieldSetEntity recordFse;
      FieldSetEntity preFse = null;
      String dataAreaFieldName;
      JSONObject dataAreaFieldConfigObj;
      String value;
      JSONObject keyObj;
      Map<String, String> replaceMap = Maps.newHashMap();
      Map<String, String> dataAreaClosestGroupStatisticsFieldValueMap = Maps.newHashMap();
      Map<String, String> dataAreaClosestGroupFieldValueMap = Maps.newHashMap();
      String closestGroupStatisticsFieldName = null;
      String closestGroupFieldName = null;
      String tempFieldName;
      String paramKey;
      boolean combineFlag;
      JSONObject dataAreaGroupFieldRecordObj = new JSONObject();
      for (int i = 0; i < recordDte.getRows(); i++) {
         recordFse = recordDte.getFieldSetEntity(i);
         // 获取头部、尾部标题区数据字段
         dataListReportService.getHeadAndTailTitleDataMap(headAndTailTitleDataMap, headAndTailFieldSet, recordFse);
         combineFlag = true;
         List<ReportColumn> columns = new ArrayList<>();
         for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            ReportColumn column = new ReportColumn();
            dataAreaFieldName = entry.getKey();
            dataAreaFieldConfigObj = entry.getValue();
            value = dataListReportService.getValue(dataAreaFieldConfigObj, recordFse, dataAreaFieldName);
            // 若是自定义字段,则放入业务数据记录中
            if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD))) {
               recordFse.setValue(dataAreaFieldName, value);
            }
            if (dataAreaGroupFieldNameList.contains(dataAreaFieldName)) {
               int curFieldIndex = dataAreaGroupFieldNameList.indexOf(dataAreaFieldName);
               // 记录分组字段
               recordDataAreaGroupField(dataAreaGroupFieldRecordObj, dataAreaGroupFieldNameList, curFieldIndex, recordFse);
               // 分组合字段并
               closestGroupFieldName = dataAreaFieldName;
               if (preFse != null && combineFlag) {
                  if (value.equals(dataAreaClosestGroupFieldValueMap.get(closestGroupFieldName))) {
                     dealReplaceMapAddRowspan(replaceMap, dataAreaGroupFieldNameList.subList(0, curFieldIndex + 1), preFse, true);
                     continue;
                  } else {
                     combineFlag = false;
                  }
               }
               dataAreaClosestGroupFieldValueMap.put(dataAreaFieldName, value);
            }
            // 分组统计
            if (dataAreaGroupStatisticsFieldNameList.contains(dataAreaFieldName)) {
               int minK = dataAreaGroupStatisticsFieldNameList.size();
               for (int j = 0; j < dataAreaGroupStatisticsFieldNameList.size(); j++) {
                  tempFieldName = dataAreaGroupStatisticsFieldNameList.get(j);
                  if (!StringUtils.isEmpty(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName)) && !recordFse.getString(tempFieldName).equals(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName))) {
                     minK = j;
                     break;
                  }
               }
               for (int k = dataAreaGroupStatisticsFieldNameList.size() - 1; k >= minK; k--) {
                  tempFieldName = dataAreaGroupStatisticsFieldNameList.get(k);
                  if (preFse != null && dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName) != null) {
                     reportData.add(getDataAreaGroupStatisticsFieldStatisticsRow(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
                     dataAreaClosestGroupStatisticsFieldValueMap.remove(tempFieldName);
                  }
               }
               closestGroupStatisticsFieldName = dataAreaFieldName;
               dataAreaClosestGroupStatisticsFieldValueMap.put(dataAreaFieldName, value);
            }
            // 非分组统计字段(普通+自定义)
            if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP)) && !StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS))) {
               addStatisticsRowInfo(statisticsMap, dataAreaGroupStatisticsFieldNameList, recordFse, dataAreaFieldConfigObj, dataAreaFieldName, value);
            }
            // 自定义统计字段(未指定统计类型,使用基础字段公式进行计算)
            if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD))
                  && StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS))
                  && !StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA))) {
               String formula = dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA);
               List<String> formFieldList = dataListReportService.getSuitContent(formula, CmnConst.REGEXP_FORM_FIELD);
               Map<String, String> formulaMap = Maps.newHashMap();
               boolean statisticsFlag = true;
               JSONObject tempObj;
               for (String formFieldInfo : formFieldList) {
                  tempObj = dataAreaFieldConfigMap.get(dataListReportService.fieldInfo2FieldName(formFieldInfo));
                  if (tempObj == null || StringUtils.isEmpty(tempObj.getString(CmnConst.ATTR_STATISTICS))) {
                     statisticsFlag = false;
                     break;
                  }
                  formulaMap.put(formFieldInfo, tempObj.getString(CmnConst.ATTR_STATISTICS));
               }
               if (statisticsFlag) {
                  addStatisticsRowInfo(statisticsMap, dataAreaGroupStatisticsFieldNameList, recordFse, dataAreaFieldConfigObj, dataAreaFieldName, value, formulaMap);
               }
            }
            if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
               keyObj = new JSONObject();
               for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                  keyObj.put(dataAreaGroupFieldName, recordFse.getString(dataAreaGroupFieldName));
                  if (dataAreaGroupFieldName.equals(dataAreaFieldName)) {
                     keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaGroupFieldName));
                     break;
                  }
               }
               paramKey = dataListReportService.concat(keyObj, CmnConst.ATTR_ROWSPAN);
               if (NumberUtil.isNumber(paramKey)) {
                  column.setRowspan(NumberUtil.parseInt(paramKey));
               } else {
                  column.addProperty(paramKey, "rowspan");
               }
               replaceMap.put(paramKey, "1");
            }
            // class
            if (!StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL))) {
               //子报表
               String url = dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL);
               url = SystemParamReplace.replaceParams(url, recordFse);
               column.setSubReport("true".equals(dataAreaFieldConfigObj.getString("~isSubReport~")));
               if (!column.isSubReport()) {
                  column.setPenetrate(true);
                  column.setPenetrateProperty(url);
               } else {
                  column.setSubReportProperty(url);
               }
            }
            String attrValue = recordFse.getString(dataAreaFieldName + CmnConst.PROMPT_REAL_VALUE_TAIL);
            if (StringUtils.isEmpty(attrValue)) {
               attrValue = recordFse.getString(dataAreaFieldName);
            }
            column.addProperty(dataAreaFieldName, attrValue);
            // 格式
            value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            column.setContent(value);
            columns.add(column);
         }
         reportData.add(columns);
         preFse = recordFse;
      }
      // 补全末尾统计
      if (!StringUtils.isEmpty(closestGroupStatisticsFieldName)) {
         int maxK = -1;
         for (int j = 0; j < dataAreaGroupStatisticsFieldNameList.size(); j++) {
            tempFieldName = dataAreaGroupStatisticsFieldNameList.get(j);
            if (closestGroupStatisticsFieldName.equals(tempFieldName)) {
               maxK = j;
            }
         }
         for (int k = maxK; k >= 0; k--) {
            tempFieldName = dataAreaGroupStatisticsFieldNameList.get(k);
            if (preFse != null) {
               reportData.add(getDataAreaGroupStatisticsFieldStatisticsRow(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
            }
         }
      }
      // 总计
      if (!StringUtils.isEmpty(totalName)) {
         reportData.add(getDataAreaTotalStatistics(statisticsMap, dataAreaFieldConfigMap, dataAreaGroupFieldNameList, preFse, dataAreaGroupFieldRecordObj, totalName));
      }
      reportData.stream().forEach(item -> {
         item.stream().forEach(column -> {
            for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
               column.replace(entry.getKey(), entry.getValue());
            }
         });
      });
      return reportData;
   }
   /**
    * 获取总计行
    *
    * @param statisticsMap               统计map
    * @param dataAreaFieldConfigMap      数据区字段缓存map
    * @param dataAreaGroupFieldNameList  数据区分组字段名称list
    * @param preFse                      最近操作的数据fse
    * @param dataAreaGroupFieldRecordObj 数据区分组字段记录obj
    * @param totalName                   总计名称
    * @return
    */
   private List<ReportColumn> getDataAreaTotalStatistics(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaGroupFieldNameList, FieldSetEntity preFse, JSONObject dataAreaGroupFieldRecordObj, String totalName) {
      List<ReportColumn> columns = new ArrayList<>();
//      StringBuilder html = new StringBuilder(512);
      String dataAreaFieldName;
      JSONObject dataAreaFieldConfigObj;
      String value;
      JSONObject keyObj = new JSONObject();
      String statisticsType;
//      html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
      if (preFse == null) {
         return null;
      }
      int index = 0;
      for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
         ReportColumn column = new ReportColumn();
         index++;
         dataAreaFieldName = entry.getKey();
         if (!dataAreaGroupFieldNameList.contains(dataAreaFieldName)) {
            dataAreaFieldConfigObj = entry.getValue();
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataAreaFieldConfigObj.getString(CmnConst.ATTR_FIELD_INFO));
            if ((!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD)) && StringUtils.isEmpty(statisticsType)) || statisticsMap.get(keyObj) == null) {
               value = "";
            } else {
               JSONObject valueObj = statisticsMap.get(keyObj);
               if (StringUtils.isEmpty(statisticsType)) {
                  statisticsType = CmnConst.ATTR_STATISTICS_DEFAULT;
               }
               if (CmnConst.ATTR_STATISTICS_AVG.equals(statisticsType) && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_AVG_TYPE))) {
                  int subCnt = dataAreaGroupFieldRecordObj.size();
                  valueObj.put(CmnConst.ATTR_STATISTICS_SUB_CNT, subCnt);
                  dataListReportService.getAvgValue(valueObj, dataAreaFieldConfigObj, false);
               }
               value = valueObj.getString(statisticsType);
            }
            // 格式
            value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            column.setContent(value);
//            html.append("<td>").append(value == null ? "" : value).append("</td>");
         } else {
            if (index == 1) {
               column.setColspan(dataAreaGroupFieldNameList.size());
               column.setContent(totalName);
//               html.append("<td colspan=\"").append(dataAreaGroupFieldNameList.size()).append("\">").append(totalName).append("</td>");
            }
         }
         columns.add(column);
      }
//      html.append("\n</tr>");
      return columns;
   }
   private List<ReportColumn> getDataAreaGroupStatisticsFieldStatisticsRow(String waitStatisticsValue, Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap,
                                                         String tempFieldName, FieldSetEntity recordFse, List<String> dataAreaGroupFieldNameList, Map<String, String> replaceMap, JSONObject dataAreaGroupFieldRecordObj) {
      List<ReportColumn> columns = new ArrayList<>();
//      StringBuilder html = new StringBuilder();
//      html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
      String dataAreaFieldName;
      JSONObject dataAreaFieldConfigObj;
      boolean flag = false;
      JSONObject keyObj = new JSONObject();
      String value;
      String statisticsType;
      int index = 0;
      List<String> needAddRowspanDataAreaGroupFieldNameList = Lists.newArrayList();
      for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
         ReportColumn column = new ReportColumn();
         index++;
         dataAreaFieldName = entry.getKey();
         if (!flag) {
            if (dataAreaGroupFieldNameList.contains(dataAreaFieldName)) {
               keyObj.put(dataAreaFieldName, recordFse.getString(dataAreaFieldName));
               if (dataAreaFieldName.equals(tempFieldName)) {
                  column.setColspan(dataAreaGroupFieldNameList.size() - index + 1);
                  column.setContent(waitStatisticsValue + CmnConst.STATISTICS_NAME);
               }
               if (!flag) {
                  needAddRowspanDataAreaGroupFieldNameList.add(dataAreaFieldName);
               }
            }
         } else {
            dataAreaFieldConfigObj = entry.getValue();
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD)) && StringUtils.isEmpty(statisticsType)) {
               if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
//                  html.append("<td></td>");
               }
            } else {
               keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
               if (statisticsMap.get(keyObj) == null) {
                  value = "";
               } else {
                  JSONObject valueObj = statisticsMap.get(keyObj);
                  if (StringUtils.isEmpty(statisticsType)) {
                     statisticsType = CmnConst.ATTR_STATISTICS_DEFAULT;
                  }
                  if (CmnConst.ATTR_STATISTICS_AVG.equals(statisticsType) && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_AVG_TYPE))) {
                     int subCnt = getAimSubCnt(keyObj, dataAreaGroupFieldNameList, dataAreaGroupFieldRecordObj);
                     valueObj.put(CmnConst.ATTR_STATISTICS_SUB_CNT, subCnt);
                     dataListReportService.getAvgValue(valueObj, dataAreaFieldConfigObj, false);
                  }
                  value = valueObj.getString(statisticsType);
               }
               // 格式
               value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
               column.setContent(value);
//               html.append("<td>").append(value == null ? "" : value).append("</td>");
            }
         }
         columns.add(column);
      }
//      html.append("\n</tr>");
      dealReplaceMapAddRowspan(replaceMap, needAddRowspanDataAreaGroupFieldNameList, recordFse, false);
      return null;
   }
   /**
    * 获取标题
@@ -500,14 +139,20 @@
    * @param dataAreaFieldConfigMap 数据区字段缓存map
    * @return
    */
   private StringBuilder getDataAreaTitleHtml(Map<String, JSONObject> dataAreaFieldConfigMap) {
      StringBuilder html = new StringBuilder(256);
      html.append("<tr class=\"").append(CmnConst.CLASS_TR_DATA_TITLE).append("\">");
   private List<ReportColumn> getDataAreaTitle(Map<String, JSONObject> dataAreaFieldConfigMap) {
      List<ReportColumn> columnRow = new ArrayList<>();
      dataAreaFieldConfigMap.forEach((dataAreaFieldName, dataAreaFieldConfigObj) -> {
         html.append("<td>").append(dataListReportService.dealTdWidth(dataAreaFieldConfigObj, dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME))).append("</td>");
         ReportColumn column = new ReportColumn();
         column.setContent(dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME));
         String width = dataAreaFieldConfigObj.getString(CmnConst.ATTR_WIDTH);
         //判断宽度是否为空并且是数字(正整数和小数都可以)
         if (StringUtils.isNotBlank(width) && width.matches("^[0-9]+(.[0-9]+)?$")) {
            //将宽度转换为double类型
            column.setColumnWidth(NumberUtil.parseInt(width));
         }
         columnRow.add(column);
      });
      html.append("</tr>");
      return html;
      return columnRow;
   }
   /**
@@ -520,7 +165,8 @@
    * @param totalName              总计名称
    * @return
    */
   private String getDataAreaDataHtml(Map<String, JSONObject> dataAreaFieldConfigMap, DataTableEntity recordDte, Map<String, Set<String>> headAndTailTitleDataMap, Set<String> headAndTailFieldSet, Map<JSONObject, JSONObject> statisticsMap, String totalName) {
   private List<List<ReportColumn>> getDataAreaData(Map<String, JSONObject> dataAreaFieldConfigMap, DataTableEntity recordDte, Map<String, Set<String>> headAndTailTitleDataMap, Set<String> headAndTailFieldSet, Map<JSONObject, JSONObject> statisticsMap, String totalName) {
      List<List<ReportColumn>> reportColumnList = Lists.newArrayList();
      // 数据区分组统计字段名称list
      List<String> dataAreaGroupStatisticsFieldNameList = Lists.newArrayList();
      // 数据区分组字段名称list
@@ -534,8 +180,9 @@
         }
      });
      StringBuilder dataAreaDataHtml = new StringBuilder(1024);
      StringBuilder curRowHtml = new StringBuilder(256);
//      StringBuilder dataAreaDataHtml = new StringBuilder(1024);
//      StringBuilder curRowHtml = new StringBuilder(256);
      List<ReportColumn> curRowColumn = new ArrayList<>();
      FieldSetEntity recordFse;
      FieldSetEntity preFse = null;
      String dataAreaFieldName;
@@ -550,15 +197,18 @@
      String tempFieldName;
      String paramKey;
      boolean combineFlag;
      ReportColumn currentColumn = new ReportColumn();
      JSONObject dataAreaGroupFieldRecordObj = new JSONObject();
      for (int i = 0; i < recordDte.getRows(); i++) {
         recordFse = recordDte.getFieldSetEntity(i);
         // 获取头部、尾部标题区数据字段
         dataListReportService.getHeadAndTailTitleDataMap(headAndTailTitleDataMap, headAndTailFieldSet, recordFse);
         curRowHtml.setLength(0);
//         curRowHtml.setLength(0);
         curRowColumn = new ArrayList<>();
         String rowUuid = IdUtil.simpleUUID();
         curRowHtml.append("\n<tr rowIndex=\"" + (i + 1) + "\" id=\"" + rowUuid + "\" class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
//         curRowHtml.append("\n<tr rowIndex=\"" + (i + 1) + "\" id=\"" + rowUuid + "\" class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
         combineFlag = true;
         for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            dataAreaFieldName = entry.getKey();
@@ -598,7 +248,7 @@
               for (int k = dataAreaGroupStatisticsFieldNameList.size() - 1; k >= minK; k--) {
                  tempFieldName = dataAreaGroupStatisticsFieldNameList.get(k);
                  if (preFse != null && dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName) != null) {
                     dataAreaDataHtml.append(getDataAreaGroupStatisticsFieldStatisticsRowHtml(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
                     reportColumnList.add(getDataAreaGroupStatisticsFieldStatisticsRow(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
                     dataAreaClosestGroupStatisticsFieldValueMap.remove(tempFieldName);
                  }
               }
@@ -640,28 +290,55 @@
                  }
               }
               paramKey = dataListReportService.concat(keyObj, CmnConst.ATTR_ROWSPAN);
               curRowHtml.append("<td rowspan=\"").append(paramKey).append("\"");
//               curRowHtml.append("<td rowspan=\"").append(paramKey).append("\"");
               currentColumn.setRowspan(paramKey);
               replaceMap.put(paramKey, "1");
            } else {
               curRowHtml.append("<td");
            }
//            else {
//               curRowHtml.append("<td");
//            }
            // class
            if (!StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL))) {
               curRowHtml.append(" class=\"").append(CmnConst.CLASS_TD_CAN_TURN).append("true".equals(dataAreaFieldConfigObj.getString("~isSubReport~")) ? "\"skipSubReport=true" : "\"").append(" router=\"").append(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL)).append("\"");
               currentColumn.setSubReport("true".equals(dataAreaFieldConfigObj.getString("~isSubReport~")));
               if (!currentColumn.isSubReport()) {
                  currentColumn.setPenetrate(true);
                  currentColumn.setPenetrateProperty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL));
               } else {
                  currentColumn.setSubReportProperty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL));
               }
//               curRowHtml.append(" class=\"").append(CmnConst.CLASS_TD_CAN_TURN).append("true".equals(dataAreaFieldConfigObj.getString("~isSubReport~")) ? "\"skipSubReport=true" : "\"").append(" router=\"").append(dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL)).append("\"");
            }
            String attrValue = recordFse.getString(dataAreaFieldName + CmnConst.PROMPT_REAL_VALUE_TAIL);
            if (StringUtils.isEmpty(attrValue)) {
               attrValue = recordFse.getString(dataAreaFieldName);
            }
            curRowHtml.append(" ").append(dataAreaFieldName).append("=\"").append(attrValue).append("\"");
            curRowHtml.append(">");
            if (!dataAreaFieldName.matches("\\d+")) {
               currentColumn.addProperty(dataAreaFieldName, attrValue);
//               curRowHtml.append(" ").append(dataAreaFieldName).append("=\"").append(attrValue).append("\"");
            }
            // 解析穿透参数
            String urlParam = dataAreaFieldConfigObj.getString(CmnConst.ATTR_URL_PARAM);
            String attrParam;
            if (!StringUtils.isEmpty(urlParam)) {
               List<String> suitFieldList = BaseUtil.getSuitContent(urlParam, "\\{#\\w+#\\}");
               for (String singleSuitFieldName : suitFieldList) {
                  attrParam = singleSuitFieldName.substring(2, singleSuitFieldName.length() - 2);
                  currentColumn.addProperty(attrParam, recordFse.getString(attrParam));
//                  curRowHtml.append(" ").append(attrParam).append("=\"").append(recordFse.getString(attrParam)).append("\"");
               }
            }
//            curRowHtml.append(">");
            // 格式
            value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            curRowHtml.append(value == null ? "" : value);
            curRowHtml.append("</td>");
//            curRowHtml.append(value == null ? "" : value);
//            curRowHtml.append("</td>");
            currentColumn.setContent(value);
            curRowColumn.add(currentColumn);
            currentColumn = new ReportColumn();
         }
         curRowHtml.append("\n</tr>");
         dataAreaDataHtml.append(curRowHtml);
//         curRowHtml.append("\n</tr>");
//         dataAreaDataHtml.append(curRowHtml);
         reportColumnList.add(curRowColumn);
         preFse = recordFse;
      }
@@ -677,22 +354,34 @@
         for (int k = maxK; k >= 0; k--) {
            tempFieldName = dataAreaGroupStatisticsFieldNameList.get(k);
            if (preFse != null) {
               dataAreaDataHtml.append(getDataAreaGroupStatisticsFieldStatisticsRowHtml(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
               reportColumnList.add(getDataAreaGroupStatisticsFieldStatisticsRow(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
            }
         }
      }
      // 总计
      if (!StringUtils.isEmpty(totalName)) {
         dataAreaDataHtml.append(getDataAreaTotalStatisticsHtml(statisticsMap, dataAreaFieldConfigMap, dataAreaGroupFieldNameList, preFse, dataAreaGroupFieldRecordObj, totalName));
         List<ReportColumn> reportColumns = getDataAreaTotalStatisticsRow(statisticsMap, dataAreaFieldConfigMap, dataAreaGroupFieldNameList, preFse, dataAreaGroupFieldRecordObj, totalName);
         if (reportColumns != null && reportColumns.size() > 0) {
            reportColumnList.add(reportColumns);
         }
      }
      //循环所有的column进行参数替换
      for (List<ReportColumn> reportColumns : reportColumnList) {
         if (reportColumns != null) {
            for (ReportColumn reportColumn : reportColumns) {
               if (reportColumn != null) {
                  for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
                     reportColumn.replace(entry.getKey(), entry.getValue());
                  }
               }
            }
         }
      }
      String str = dataAreaDataHtml.toString();
      for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
         str = str.replace(entry.getKey(), entry.getValue());
      }
      return str;
      return reportColumnList;
   }
   /**
@@ -781,10 +470,9 @@
    * @param dataAreaGroupFieldRecordObj 数据区分组字段记录obj
    * @return
    */
   private StringBuilder getDataAreaGroupStatisticsFieldStatisticsRowHtml(String waitStatisticsValue, Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap,
                                                         String tempFieldName, FieldSetEntity recordFse, List<String> dataAreaGroupFieldNameList, Map<String, String> replaceMap, JSONObject dataAreaGroupFieldRecordObj) {
      StringBuilder html = new StringBuilder();
      html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
   private List<ReportColumn> getDataAreaGroupStatisticsFieldStatisticsRow(String waitStatisticsValue, Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap,
                                                         String tempFieldName, FieldSetEntity recordFse, List<String> dataAreaGroupFieldNameList, Map<String, String> replaceMap, JSONObject dataAreaGroupFieldRecordObj) {
      List<ReportColumn> columnRow = new ArrayList<>();
      String dataAreaFieldName;
      JSONObject dataAreaFieldConfigObj;
      boolean flag = false;
@@ -800,8 +488,12 @@
            if (dataAreaGroupFieldNameList.contains(dataAreaFieldName)) {
               keyObj.put(dataAreaFieldName, recordFse.getString(dataAreaFieldName));
               if (dataAreaFieldName.equals(tempFieldName)) {
                  html.append("<td colspan=\"").append(dataAreaGroupFieldNameList.size() - index + 1).append("\">").append(waitStatisticsValue).append(CmnConst.STATISTICS_NAME).append("</td>");
                  ReportColumn column = new ReportColumn();
                  column.setColspan(dataAreaGroupFieldNameList.size() - index + 1);
                  column.setContent(waitStatisticsValue);
//                  html.append("<td colspan=\"").append(dataAreaGroupFieldNameList.size() - index + 1).append("\">").append(waitStatisticsValue).append(CmnConst.STATISTICS_NAME).append("</td>");
                  flag = true;
                  columnRow.add(column);
               }
               if (!flag) {
                  needAddRowspanDataAreaGroupFieldNameList.add(dataAreaFieldName);
@@ -812,7 +504,8 @@
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD)) && StringUtils.isEmpty(statisticsType)) {
               if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
                  html.append("<td></td>");
//                  html.append("<td></td>");
                  columnRow.add(new ReportColumn());
               }
            } else {
               keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
@@ -832,13 +525,13 @@
               }
               // 格式
               value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
               html.append("<td>").append(value == null ? "" : value).append("</td>");
//               html.append("<td>").append(value == null ? "" : value).append("</td>");
               columnRow.add(new ReportColumn(value == null ? "" : value));
            }
         }
      }
      html.append("\n</tr>");
      dealReplaceMapAddRowspan(replaceMap, needAddRowspanDataAreaGroupFieldNameList, recordFse, false);
      return html;
      return columnRow;
   }
   /**
@@ -852,16 +545,16 @@
    * @param totalName                   总计名称
    * @return
    */
   private String getDataAreaTotalStatisticsHtml(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaGroupFieldNameList, FieldSetEntity preFse, JSONObject dataAreaGroupFieldRecordObj, String totalName) {
      StringBuilder html = new StringBuilder(512);
   private List<ReportColumn> getDataAreaTotalStatisticsRow(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaGroupFieldNameList, FieldSetEntity preFse, JSONObject dataAreaGroupFieldRecordObj, String totalName) {
      List<ReportColumn> reportColumns = new ArrayList<>();
      String dataAreaFieldName;
      JSONObject dataAreaFieldConfigObj;
      String value;
      JSONObject keyObj = new JSONObject();
      String statisticsType;
      html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
//      html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
      if (preFse == null) {
         return html.toString();
         return null;
      }
      int index = 0;
      for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
@@ -887,15 +580,17 @@
            }
            // 格式
            value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            html.append("<td>").append(value == null ? "" : value).append("</td>");
            reportColumns.add(new ReportColumn(value == null ? "" : value));
         } else {
            if (index == 1) {
               html.append("<td colspan=\"").append(dataAreaGroupFieldNameList.size()).append("\">").append(totalName).append("</td>");
               ReportColumn column = new ReportColumn();
               column.setColspan(dataAreaGroupFieldNameList.size());
               column.setContent(totalName);
               reportColumns.add(column);
            }
         }
      }
      html.append("\n</tr>");
      return html.toString();
      return reportColumns;
   }
   /**