1821349743@qq.com
2023-04-08 0d63f52d84e393204af3ba5bce86bdfddf936be8
修改报表加载返回html更改为数据
已添加2个文件
已修改5个文件
4259 ■■■■■ 文件已修改
src/main/java/com/product/server/report/controller/DataListReportController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/entity/ReportColumn.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/entity/ReportEntity.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/service/CommonReportService.java 1443 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/service/DataListReportService.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/service/GroupReportService.java 2256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/service/ReportConfigService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/product/server/report/controller/DataListReportController.java
@@ -89,7 +89,7 @@
                SpringMVCContextHolder.getSystemLogger().error(SystemCode.SYSTEM_FORM_NODATA.getValue(), SystemCode.SYSTEM_FORM_NODATA.getText());
                return this.error(SystemCode.SYSTEM_FORM_NODATA.getValue(), SystemCode.SYSTEM_FORM_NODATA.getText());
            }
            return BaseUtil.success(dataListReportService.getReport(fse));
            return BaseUtil.success(dataListReportService.getReportEntity(fse));
        } catch (BaseException e) {
            SpringMVCContextHolder.getSystemLogger().error(e);
            return this.error(e.getCode(), e.getMessageInfo());
src/main/java/com/product/server/report/entity/ReportColumn.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,120 @@
package com.product.server.report.entity;
import com.product.common.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
 * @Author cheng
 * @Date 2023/4/3 11:34
 * @Desc æŠ¥è¡¨å•元格
 */
public class ReportColumn {
    //跨列
    private int colspan = 1;
    //跨行
    private int rowspan = 1;
    //单元格内容
    private String content;
    //穿透字段
    private boolean penetrate;
    //子报表
    private boolean subReport;
    //穿透属性
    private String penetrateProperty;
    //子报表属性
    private String subReportProperty;
    //单元格宽度 px
    private int columnWidth;
    private Map<String, String> otherParams = new HashMap<>();
    public void replace(String key, String value) {
        if (!otherParams.isEmpty()) {
            Map<String, String> otherParams = this.otherParams;
            this.otherParams = new HashMap<>();
            otherParams.forEach((k, v) -> {
                this.otherParams.put(k.replace(key, value), v.replace(key, value));
            });
        }
        if(!StringUtils.isEmpty(subReportProperty)){
            subReportProperty=subReportProperty.replace(key,value);
        }
        if(!StringUtils.isEmpty(penetrateProperty)){
            penetrateProperty=penetrateProperty.replace(key,value);
        }
        if(!StringUtils.isEmpty(content)){
            content=content.replace(key,value);
        }
    }
    public void addProperty(String key, String value) {
        otherParams.put(key, value);
    }
    public int getColspan() {
        return colspan;
    }
    public void setColspan(int colspan) {
        this.colspan = colspan;
    }
    public int getRowspan() {
        return rowspan;
    }
    public void setRowspan(int rowspan) {
        this.rowspan = rowspan;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public boolean isPenetrate() {
        return penetrate;
    }
    public void setPenetrate(boolean penetrate) {
        this.penetrate = penetrate;
    }
    public boolean isSubReport() {
        return subReport;
    }
    public void setSubReport(boolean subReport) {
        this.subReport = subReport;
    }
    public String getPenetrateProperty() {
        return penetrateProperty;
    }
    public void setPenetrateProperty(String penetrateProperty) {
        this.penetrateProperty = penetrateProperty;
    }
    public String getSubReportProperty() {
        return subReportProperty;
    }
    public void setSubReportProperty(String subReportProperty) {
        this.subReportProperty = subReportProperty;
    }
    public int getColumnWidth() {
        return columnWidth;
    }
    public void setColumnWidth(int columnWidth) {
        this.columnWidth = columnWidth;
    }
}
src/main/java/com/product/server/report/entity/ReportEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
package com.product.server.report.entity;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
 * @Author cheng
 * @Date 2023/4/3 11:33
 * @Desc æŠ¥è¡¨å±•示
 */
public class ReportEntity {
    //前端使用
    private String entityType = "report";
    //头部标题 ï¼ˆåŒ…含内容头)
    private List<List<ReportColumn>> reportHeader;
    //内容
    private List<List<ReportColumn>> reportData;
    //底部标题
    private List<List<ReportColumn>> reportTail;
    //报表类型uuid
    private String reportType;
    //默认过滤条件
    private Map<String, Map<String, String>> filterInfo;
    //字段信息
    private JSONArray systemFieldMeta;
    /************分页参数*************/
    private boolean isPage;
    private int pageSize;
    private int pageIndex;
    private int totalPage;
    /************分页参数************
     /**
     * æ·»åŠ ä¸€è¡Œè¡¨å¤´
     *
     * @param reportColumnArray
     */
    public void addReportHeader(List<ReportColumn> reportColumnArray) {
        if (CollectionUtil.isEmpty(reportColumnArray)) {
            return;
        }
        if (CollectionUtil.isEmpty(this.reportHeader)) {
            reportHeader = new ArrayList<>();
        }
        reportHeader.add(reportColumnArray);
    }
    /**
     * æ·»åŠ ä¸€è¡Œæ•°æ®
     *
     * @param reportDataArray
     */
    private void addReportData(List<ReportColumn> reportDataArray) {
        if (CollectionUtil.isEmpty(reportDataArray)) {
            return;
        }
        if (CollectionUtil.isEmpty(this.reportData)) {
            reportData = new ArrayList<>();
        }
        reportData.add(reportDataArray);
    }
    public String getEntityType() {
        return entityType;
    }
    public List<List<ReportColumn>> getReportHeader() {
        return reportHeader;
    }
    public void setReportHeader(List<List<ReportColumn>> reportHeader) {
        this.reportHeader = reportHeader;
    }
    public List<List<ReportColumn>> getReportData() {
        return reportData;
    }
    public void setReportData(List<List<ReportColumn>> reportData) {
        this.reportData = reportData;
    }
    public List<List<ReportColumn>> getReportTail() {
        return reportTail;
    }
    public void setReportTail(List<List<ReportColumn>> reportTail) {
        this.reportTail = reportTail;
    }
    public String getReportType() {
        return reportType;
    }
    public void setReportType(String reportType) {
        this.reportType = reportType;
    }
    public Map<String, Map<String, String>> getFilterInfo() {
        return filterInfo;
    }
    public void setFilterInfo(Map<String, Map<String, String>> filterInfo) {
        this.filterInfo = filterInfo;
    }
    public JSONArray getSystemFieldMeta() {
        return systemFieldMeta;
    }
    public void setSystemFieldMeta(JSONArray systemFieldMeta) {
        this.systemFieldMeta = systemFieldMeta;
    }
    public boolean isPage() {
        return isPage;
    }
    public void setPage(boolean page) {
        isPage = page;
    }
    public int getPageSize() {
        return pageSize;
    }
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
    public int getPageIndex() {
        return pageIndex;
    }
    public void setPageIndex(int pageIndex) {
        this.pageIndex = pageIndex;
    }
    public int getTotalPage() {
        return totalPage;
    }
    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }
}
src/main/java/com/product/server/report/service/CommonReportService.java
@@ -1,6 +1,8 @@
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;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -11,7 +13,10 @@
import com.product.core.service.support.AbstractBaseService;
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;
@@ -26,556 +31,940 @@
 */
@Component
public class CommonReportService extends AbstractBaseService {
    @Autowired
    private DataListReportService dataListReportService;
    @Autowired
    private DataListReportService dataListReportService;
    /**
     * æŠ¥è¡¨-解析
     *
     * @param recordDte           ä¸šåŠ¡æ•°æ®dte
     * @param totalStatisticsFlag æ˜¯å¦åˆè®¡æ ‡è¯†
     * @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);
    /**
     * æŠ¥è¡¨-解析
     *
     * @param recordDte           ä¸šåŠ¡æ•°æ®dte
     * @param totalStatisticsFlag æ˜¯å¦åˆè®¡æ ‡è¯†
     * @param reportConfigMap     æŠ¥è¡¨é…ç½®ç¼“å­˜map
     * @param tableStyle
     * @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);
        // css
        StringBuilder cssHtml = dataListReportService.getCssHtml();
        int totalColCount = reportConfigMap.get(0).size();
        // æ•°æ®åŒº
        Map<String, Set<String>> headAndTailTitleDataMap = Maps.newHashMap();
        String dataAreaHtml = getDataAreaHtml(reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
        // å¤´éƒ¨æ ‡é¢˜åŒº
        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");
        }
        int totalColCount = reportConfigMap.get(0).size();
        // å¤´éƒ¨æ ‡é¢˜åŒº
        StringBuilder headTitleHtml = null;
        if (!reportConfigMap.get(1).isEmpty()) {
            headTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
        }
        if (!CollectionUtil.isEmpty(headTitleRows)) {
            List<List<ReportColumn>> reportHeader = report.getReportHeader();
            headTitleRows.addAll(reportHeader);
            report.setReportHeader(headTitleRows);
        }
        if (!CollectionUtil.isEmpty(tailTitleRows)) {
            report.setReportTail(tailTitleRows);
        }
        // åº•部标题区
        StringBuilder tailTitleHtml = null;
        if (!reportConfigMap.get(3).isEmpty()) {
            tailTitleHtml = dataListReportService.getTitleHtml(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>");
        return report;
    }
        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 totalStatisticsFlag æ˜¯å¦åˆè®¡æ ‡è¯†
     * @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);
    /**
     * èŽ·å–æŠ¥è¡¨æ•°æ®åŒºhtml
     *
     * @param reportConfigMap         æŠ¥è¡¨ç¼“å­˜map
     * @param headAndTailTitleDataMap å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºæ•°æ®å­—段map
     * @param recordDte               ä¸šåŠ¡æ•°æ®é›†åˆ
     * @param totalName               æ€»è®¡åç§°ï¼Œéžç©º-需要总计
     * @return
     */
    private String getDataAreaHtml(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();
        // css
        StringBuilder cssHtml = dataListReportService.getCssHtml();
        StringBuilder dataAreaHtml = new StringBuilder(1024);
        // æ ‡é¢˜
        dataAreaHtml.append(getDataAreaTitleHtml(dataAreaFieldConfigMap));
        // å†…容
        dataAreaHtml.append(getDataAreaDataHtml(dataAreaFieldConfigMap, recordDte, headAndTailTitleDataMap, headAndTailFieldSet, statisticsMap, totalName));
        return dataAreaHtml.toString();
    }
        // æ•°æ®åŒº
        Map<String, Set<String>> headAndTailTitleDataMap = Maps.newHashMap();
        String dataAreaHtml = getDataAreaHtml(reportConfigMap, headAndTailTitleDataMap, recordDte, totalName);
    /**
     * èŽ·å–æ ‡é¢˜
     *
     * @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("\">");
        dataAreaFieldConfigMap.forEach((dataAreaFieldName, dataAreaFieldConfigObj) -> {
            html.append("<td>").append(dataListReportService.dealTdWidth(dataAreaFieldConfigObj, dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME))).append("</td>");
        });
        html.append("</tr>");
        return html;
    }
        int totalColCount = reportConfigMap.get(0).size();
        // å¤´éƒ¨æ ‡é¢˜åŒº
        StringBuilder headTitleHtml = null;
        if (!reportConfigMap.get(1).isEmpty()) {
            headTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
        }
    /**
     * èŽ·å–å†…å®¹ï¼ŒåŒ…å«ç»Ÿè®¡
     *
     * @param dataAreaFieldConfigMap æ•°æ®åŒºå­—段缓存map
     * @param recordDte              ä¸šåŠ¡æ•°æ®é›†åˆ
     * @param headAndTailFieldSet    å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºå­—段set
     * @param statisticsMap          ç»Ÿè®¡map
     * @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) {
        // æ•°æ®åŒºåˆ†ç»„统计字段名称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);
                }
            }
        });
        // åº•部标题区
        StringBuilder tailTitleHtml = null;
        if (!reportConfigMap.get(3).isEmpty()) {
            tailTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(3), totalColCount, headAndTailTitleDataMap, "tail");
        }
        StringBuilder dataAreaDataHtml = new StringBuilder(1024);
        StringBuilder curRowHtml = new StringBuilder(256);
        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);
        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>");
            curRowHtml.setLength(0);
            String rowUuid = IdUtil.simpleUUID();
            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();
                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);
        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;
    }
                    // åˆ†ç»„合字段并
                    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) {
                            dataAreaDataHtml.append(getDataAreaGroupStatisticsFieldStatisticsRowHtml(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);
                    curRowHtml.append("<td rowspan=\"").append(paramKey).append("\"");
                    replaceMap.put(paramKey, "1");
                } 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("\"");
                }
                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(">");
                // æ ¼å¼
                value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
                curRowHtml.append(value == null ? "" : value);
                curRowHtml.append("</td>");
            }
            curRowHtml.append("\n</tr>");
            dataAreaDataHtml.append(curRowHtml);
            preFse = recordFse;
        }
    /**
     * èŽ·å–æŠ¥è¡¨æ•°æ®åŒºhtml
     *
     * @param reportConfigMap         æŠ¥è¡¨ç¼“å­˜map
     * @param headAndTailTitleDataMap å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºæ•°æ®å­—段map
     * @param recordDte               ä¸šåŠ¡æ•°æ®é›†åˆ
     * @param totalName               æ€»è®¡åç§°ï¼Œéžç©º-需要总计
     * @return
     */
    private String getDataAreaHtml(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();
        // è¡¥å…¨æœ«å°¾ç»Ÿè®¡
        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) {
                    dataAreaDataHtml.append(getDataAreaGroupStatisticsFieldStatisticsRowHtml(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
                }
            }
        }
        StringBuilder dataAreaHtml = new StringBuilder(1024);
        // æ ‡é¢˜
        dataAreaHtml.append(getDataAreaTitleHtml(dataAreaFieldConfigMap));
        // å†…容
        dataAreaHtml.append(getDataAreaDataHtml(dataAreaFieldConfigMap, recordDte, headAndTailTitleDataMap, headAndTailFieldSet, statisticsMap, totalName));
        return dataAreaHtml.toString();
    }
        // æ€»è®¡
        if (!StringUtils.isEmpty(totalName)) {
            dataAreaDataHtml.append(getDataAreaTotalStatisticsHtml(statisticsMap, dataAreaFieldConfigMap, dataAreaGroupFieldNameList, preFse, dataAreaGroupFieldRecordObj, totalName));
        }
    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;
    }
        String str = dataAreaDataHtml.toString();
        for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
            str = str.replace(entry.getKey(), entry.getValue());
        }
    private List<ReportColumn> getReportHeader(Map<String, JSONObject> dataAreaFieldConfigMap) {
        if (CollectionUtil.isEmpty(dataAreaFieldConfigMap)) {
            return null;
        }
        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);
        });
        return reportColumnList;
    }
        return str;
    }
    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);
                }
            }
        });
    /**
     * æ·»åŠ åˆ†ç»„ç»Ÿè®¡ä¿¡æ¯
     * @param statisticsMap                         ç»Ÿè®¡map
     * @param dataAreaGroupStatisticsFieldNameList  æ•°æ®åŒºåˆ†ç»„统计字段名称list
     * @param recordFse                             ä¸šåŠ¡æ•°æ®fse
     * @param dataAreaFieldConfigObj                æ•°æ®åŒºå­—段obj
     * @param dataAreaFieldName                     æ•°æ®åŒºå­—段名称
     * @param value                                 å€¼
     * @param formulaMap                            å…¬å¼map,字段info和统计类型的映射
     */
    private void addStatisticsRowInfo(Map<JSONObject, JSONObject> statisticsMap, List<String> dataAreaGroupStatisticsFieldNameList, FieldSetEntity recordFse, JSONObject dataAreaFieldConfigObj, String dataAreaFieldName, String value, Map<String, String> formulaMap) {
        JSONObject keyObj = new JSONObject();
        keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
        String statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
        if (StringUtils.isEmpty(statisticsType) && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
            statisticsType = CmnConst.ATTR_STATISTICS_DEFAULT;
        }
        String statisticsValue;
        String formula = dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA);
        JSONObject formulaFieldKeyObj = new JSONObject();
        // æ€»è®¡
        JSONObject cloneKeyObj = (JSONObject) keyObj.clone();
        JSONObject valueObj = statisticsMap.computeIfAbsent(cloneKeyObj, k -> new JSONObject());
        statisticsValue = getStatisticsValue(statisticsMap, formulaMap, formulaFieldKeyObj, formula, value);
        statisticsMap.put(keyObj, dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, statisticsValue, statisticsType, true));
        // åˆ†ç»„统计
        for (String dataAreaGroupStatisticsFieldName : dataAreaGroupStatisticsFieldNameList) {
            keyObj.put(dataAreaGroupStatisticsFieldName, recordFse.getString(dataAreaGroupStatisticsFieldName));
            formulaFieldKeyObj.put(dataAreaGroupStatisticsFieldName, recordFse.getString(dataAreaGroupStatisticsFieldName));
            cloneKeyObj = (JSONObject) keyObj.clone();
            valueObj = statisticsMap.computeIfAbsent(cloneKeyObj, k -> new JSONObject());
            statisticsValue = getStatisticsValue(statisticsMap, formulaMap, formulaFieldKeyObj, formula, value);
            statisticsMap.put(keyObj, dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, statisticsValue, statisticsType, true));
        }
    }
    private void addStatisticsRowInfo(Map<JSONObject, JSONObject> statisticsMap, List<String> dataAreaGroupStatisticsFieldNameList, FieldSetEntity recordFse, JSONObject dataAreaFieldConfigObj, String dataAreaFieldName, String value) {
        addStatisticsRowInfo(statisticsMap, dataAreaGroupStatisticsFieldNameList, recordFse, dataAreaFieldConfigObj, dataAreaFieldName, value, null);
    }
        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);
    /**
     * èŽ·å–ç»Ÿè®¡è¡Œçš„å…¬å¼è®¡ç®—å€¼
     * @param statisticsMap         ç»Ÿè®¡map
     * @param formulaMap            å…¬å¼map
     * @param formulaFieldKeyObj    å…¬å¼å­—段keyobj
     * @param formula               å…¬å¼
     * @param value                 åŽŸæœ¬çš„å€¼
     * @return
     */
    private String getStatisticsValue(Map<JSONObject, JSONObject> statisticsMap, Map<String, String> formulaMap, JSONObject formulaFieldKeyObj, String formula, String value) {
        String statisticsValue = "";
        String formulaFieldValue;
        if (formulaMap != null && !formulaMap.isEmpty()) {
            for (Map.Entry<String, String> entry : formulaMap.entrySet()) {
                formulaFieldKeyObj.put(CmnConst.ATTR_FIELD_INFO, entry.getKey());
                formulaFieldValue = statisticsMap.get(formulaFieldKeyObj).getString(entry.getValue());
                if (formulaFieldValue != null && formulaFieldValue.matches(CmnConst.REGEXP_NUMBER)) {
                    formulaFieldValue += "d";
                }
                formula = formula.replace(entry.getKey(),  StringUtils.isEmpty(formulaFieldValue) ? "" : formulaFieldValue);
            }
            try {
                statisticsValue = BaseUtil.executeExpression(formula, Maps.newHashMap()).toString();
            } catch (Exception e) {
                SpringMVCContextHolder.getSystemLogger().error(e);
                statisticsValue = "";
            }
        }
        return StringUtils.isEmpty(statisticsValue) ? value : statisticsValue;
    }
            combineFlag = true;
    /**
     * èŽ·å–æ•°æ®åŒºåˆ†ç»„ç»Ÿè®¡å­—æ®µç»Ÿè®¡è¡Œhtml
     *
     * @param waitStatisticsValue         å¾…统计的值
     * @param statisticsMap               ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap      æ•°æ®åŒºå­—段缓存map
     * @param tempFieldName               æœ¬æ¬¡æ“ä½œçš„字段名称
     * @param recordFse                   ä¸šåŠ¡æ•°æ®è®°å½•
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param replaceMap                  æ›¿æ¢map
     * @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    ");
        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()) {
            index++;
            dataAreaFieldName = entry.getKey();
            if (!flag) {
                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>");
                        flag = true;
                    }
                    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);
                    html.append("<td>").append(value == null ? "" : value).append("</td>");
                }
            }
        }
        html.append("\n</tr>");
        dealReplaceMapAddRowspan(replaceMap, needAddRowspanDataAreaGroupFieldNameList, recordFse, false);
        return html;
    }
            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);
    /**
     * èŽ·å–æ€»è®¡html
     *
     * @param statisticsMap               ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap      æ•°æ®åŒºå­—段缓存map
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param preFse                      æœ€è¿‘操作的数据fse
     * @param dataAreaGroupFieldRecordObj æ•°æ®åŒºåˆ†ç»„字段记录obj
     * @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);
        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 html.toString();
        }
        int index = 0;
        for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            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);
                html.append("<td>").append(value == null ? "" : value).append("</td>");
            } else {
                if (index == 1) {
                    html.append("<td colspan=\"").append(dataAreaGroupFieldNameList.size()).append("\">").append(totalName).append("</td>");
                }
            }
        }
        html.append("\n</tr>");
        return html.toString();
    }
                    // åˆ†ç»„合字段并
                    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);
                    column.setRowspan(NumberUtil.parseInt(paramKey));
                    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;
        }
    /**
     * å¤„理参数替换map-添加数据区分组字段的占行
     *
     * @param replaceMap                            æ›¿æ¢map
     * @param needAddRowspanDataAreaGroupFieldNames éœ€è¦æ·»åŠ å è¡Œçš„æ•°æ®åŒºåˆ†ç»„å­—æ®µåç§°é›†åˆ
     * @param recordFse                             ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void dealReplaceMapAddRowspan(Map<String, String> replaceMap, Collection<String> needAddRowspanDataAreaGroupFieldNames, FieldSetEntity recordFse, boolean commonFieldFlag) {
        JSONObject keyObj = new JSONObject();
        String value;
        int rowspan;
        String paramKey;
        int index = 0;
        for (String fieldName : needAddRowspanDataAreaGroupFieldNames) {
            index++;
            keyObj.put(fieldName, recordFse.getString(fieldName));
            if (commonFieldFlag && index != needAddRowspanDataAreaGroupFieldNames.size()) {
                continue;
            }
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(fieldName));
            paramKey = dataListReportService.concat(keyObj, CmnConst.ATTR_ROWSPAN);
            value = replaceMap.get(paramKey);
            rowspan = StringUtils.isEmpty(value) ? 1 : Integer.parseInt(value) + 1;
            replaceMap.put(paramKey, String.valueOf(rowspan));
        }
    }
        // è¡¥å…¨æœ«å°¾ç»Ÿè®¡
        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));
                }
            }
        }
    /**
     * è®°å½•分组字段
     *
     * @param dataAreaGroupFieldRecordObj æ•°æ®åŒºåˆ†ç»„字段记录obj
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param curFieldIndex               å½“前字段下标
     * @param recordFse                   ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void recordDataAreaGroupField(JSONObject dataAreaGroupFieldRecordObj, List<String> dataAreaGroupFieldNameList, int curFieldIndex, FieldSetEntity recordFse) {
        String tempFieldName;
        JSONObject parentObj = dataAreaGroupFieldRecordObj;
        JSONObject subObj;
        String value;
        for (int j = 0; j <= curFieldIndex; j++) {
            tempFieldName = dataAreaGroupFieldNameList.get(j);
            value = recordFse.getString(tempFieldName);
            subObj = parentObj.getJSONObject(value);
            if (subObj == null) {
                subObj = new JSONObject();
                parentObj.put(value, subObj);
            }
            parentObj = subObj;
        }
    }
        // æ€»è®¡
        if (!StringUtils.isEmpty(totalName)) {
            reportData.add(getDataAreaTotalStatistics(statisticsMap, dataAreaFieldConfigMap, dataAreaGroupFieldNameList, preFse, dataAreaGroupFieldRecordObj, totalName));
        }
        for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
            reportData.stream().forEach(item -> {
                item.stream().forEach(column -> {
                    column.replace(entry.getKey(), entry.getValue());
                });
            });
        }
    /**
     * èŽ·å–æŒ‡å®šçš„ä¸‹çº§åˆ†ç»„é¡¹
     *
     * @param keyObj                      æŒ‡å®šçš„key
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param dataAreaGroupFieldRecordObj æ•°æ®åŒºåˆ†ç»„字段记录obj
     * @return
     */
    private int getAimSubCnt(JSONObject keyObj, List<String> dataAreaGroupFieldNameList, JSONObject dataAreaGroupFieldRecordObj) {
        String value;
        JSONObject parentObj = dataAreaGroupFieldRecordObj;
        JSONObject subObj = null;
        for (String fieldName : dataAreaGroupFieldNameList) {
            value = keyObj.getString(fieldName);
            if (value != null) {
                subObj = parentObj.getJSONObject(value);
            } else {
                break;
            }
            parentObj = subObj;
        }
        return subObj == null ? 0 : subObj.size();
    }
        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;
    }
    /**
     * èŽ·å–æ ‡é¢˜
     *
     * @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("\">");
        dataAreaFieldConfigMap.forEach((dataAreaFieldName, dataAreaFieldConfigObj) -> {
            html.append("<td>").append(dataListReportService.dealTdWidth(dataAreaFieldConfigObj, dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME))).append("</td>");
        });
        html.append("</tr>");
        return html;
    }
    /**
     * èŽ·å–å†…å®¹ï¼ŒåŒ…å«ç»Ÿè®¡
     *
     * @param dataAreaFieldConfigMap æ•°æ®åŒºå­—段缓存map
     * @param recordDte              ä¸šåŠ¡æ•°æ®é›†åˆ
     * @param headAndTailFieldSet    å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºå­—段set
     * @param statisticsMap          ç»Ÿè®¡map
     * @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) {
        // æ•°æ®åŒºåˆ†ç»„统计字段名称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);
                }
            }
        });
        StringBuilder dataAreaDataHtml = new StringBuilder(1024);
        StringBuilder curRowHtml = new StringBuilder(256);
        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);
            curRowHtml.setLength(0);
            String rowUuid = IdUtil.simpleUUID();
            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();
                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) {
                            dataAreaDataHtml.append(getDataAreaGroupStatisticsFieldStatisticsRowHtml(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);
                    curRowHtml.append("<td rowspan=\"").append(paramKey).append("\"");
                    replaceMap.put(paramKey, "1");
                } 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("\"");
                }
                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(">");
                // æ ¼å¼
                value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
                curRowHtml.append(value == null ? "" : value);
                curRowHtml.append("</td>");
            }
            curRowHtml.append("\n</tr>");
            dataAreaDataHtml.append(curRowHtml);
            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) {
                    dataAreaDataHtml.append(getDataAreaGroupStatisticsFieldStatisticsRowHtml(dataAreaClosestGroupStatisticsFieldValueMap.get(tempFieldName), statisticsMap, dataAreaFieldConfigMap, tempFieldName, preFse, dataAreaGroupFieldNameList, replaceMap, dataAreaGroupFieldRecordObj));
                }
            }
        }
        // æ€»è®¡
        if (!StringUtils.isEmpty(totalName)) {
            dataAreaDataHtml.append(getDataAreaTotalStatisticsHtml(statisticsMap, dataAreaFieldConfigMap, dataAreaGroupFieldNameList, preFse, dataAreaGroupFieldRecordObj, totalName));
        }
        String str = dataAreaDataHtml.toString();
        for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
            str = str.replace(entry.getKey(), entry.getValue());
        }
        return str;
    }
    /**
     * æ·»åŠ åˆ†ç»„ç»Ÿè®¡ä¿¡æ¯
     *
     * @param statisticsMap                        ç»Ÿè®¡map
     * @param dataAreaGroupStatisticsFieldNameList æ•°æ®åŒºåˆ†ç»„统计字段名称list
     * @param recordFse                            ä¸šåŠ¡æ•°æ®fse
     * @param dataAreaFieldConfigObj               æ•°æ®åŒºå­—段obj
     * @param dataAreaFieldName                    æ•°æ®åŒºå­—段名称
     * @param value                                å€¼
     * @param formulaMap                           å…¬å¼map,字段info和统计类型的映射
     */
    private void addStatisticsRowInfo(Map<JSONObject, JSONObject> statisticsMap, List<String> dataAreaGroupStatisticsFieldNameList, FieldSetEntity recordFse, JSONObject dataAreaFieldConfigObj, String dataAreaFieldName, String value, Map<String, String> formulaMap) {
        JSONObject keyObj = new JSONObject();
        keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
        String statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
        if (StringUtils.isEmpty(statisticsType) && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
            statisticsType = CmnConst.ATTR_STATISTICS_DEFAULT;
        }
        String statisticsValue;
        String formula = dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA);
        JSONObject formulaFieldKeyObj = new JSONObject();
        // æ€»è®¡
        JSONObject cloneKeyObj = (JSONObject) keyObj.clone();
        JSONObject valueObj = statisticsMap.computeIfAbsent(cloneKeyObj, k -> new JSONObject());
        statisticsValue = getStatisticsValue(statisticsMap, formulaMap, formulaFieldKeyObj, formula, value);
        statisticsMap.put(keyObj, dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, statisticsValue, statisticsType, true));
        // åˆ†ç»„统计
        for (String dataAreaGroupStatisticsFieldName : dataAreaGroupStatisticsFieldNameList) {
            keyObj.put(dataAreaGroupStatisticsFieldName, recordFse.getString(dataAreaGroupStatisticsFieldName));
            formulaFieldKeyObj.put(dataAreaGroupStatisticsFieldName, recordFse.getString(dataAreaGroupStatisticsFieldName));
            cloneKeyObj = (JSONObject) keyObj.clone();
            valueObj = statisticsMap.computeIfAbsent(cloneKeyObj, k -> new JSONObject());
            statisticsValue = getStatisticsValue(statisticsMap, formulaMap, formulaFieldKeyObj, formula, value);
            statisticsMap.put(keyObj, dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, statisticsValue, statisticsType, true));
        }
    }
    private void addStatisticsRowInfo(Map<JSONObject, JSONObject> statisticsMap, List<String> dataAreaGroupStatisticsFieldNameList, FieldSetEntity recordFse, JSONObject dataAreaFieldConfigObj, String dataAreaFieldName, String value) {
        addStatisticsRowInfo(statisticsMap, dataAreaGroupStatisticsFieldNameList, recordFse, dataAreaFieldConfigObj, dataAreaFieldName, value, null);
    }
    /**
     * èŽ·å–ç»Ÿè®¡è¡Œçš„å…¬å¼è®¡ç®—å€¼
     *
     * @param statisticsMap      ç»Ÿè®¡map
     * @param formulaMap         å…¬å¼map
     * @param formulaFieldKeyObj å…¬å¼å­—段keyobj
     * @param formula            å…¬å¼
     * @param value              åŽŸæœ¬çš„å€¼
     * @return
     */
    private String getStatisticsValue(Map<JSONObject, JSONObject> statisticsMap, Map<String, String> formulaMap, JSONObject formulaFieldKeyObj, String formula, String value) {
        String statisticsValue = "";
        String formulaFieldValue;
        if (formulaMap != null && !formulaMap.isEmpty()) {
            for (Map.Entry<String, String> entry : formulaMap.entrySet()) {
                formulaFieldKeyObj.put(CmnConst.ATTR_FIELD_INFO, entry.getKey());
                formulaFieldValue = statisticsMap.get(formulaFieldKeyObj).getString(entry.getValue());
                if (formulaFieldValue != null && formulaFieldValue.matches(CmnConst.REGEXP_NUMBER)) {
                    formulaFieldValue += "d";
                }
                formula = formula.replace(entry.getKey(), StringUtils.isEmpty(formulaFieldValue) ? "" : formulaFieldValue);
            }
            try {
                statisticsValue = BaseUtil.executeExpression(formula, Maps.newHashMap()).toString();
            } catch (Exception e) {
                SpringMVCContextHolder.getSystemLogger().error(e);
                statisticsValue = "";
            }
        }
        return StringUtils.isEmpty(statisticsValue) ? value : statisticsValue;
    }
    /**
     * èŽ·å–æ•°æ®åŒºåˆ†ç»„ç»Ÿè®¡å­—æ®µç»Ÿè®¡è¡Œhtml
     *
     * @param waitStatisticsValue         å¾…统计的值
     * @param statisticsMap               ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap      æ•°æ®åŒºå­—段缓存map
     * @param tempFieldName               æœ¬æ¬¡æ“ä½œçš„字段名称
     * @param recordFse                   ä¸šåŠ¡æ•°æ®è®°å½•
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param replaceMap                  æ›¿æ¢map
     * @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    ");
        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()) {
            index++;
            dataAreaFieldName = entry.getKey();
            if (!flag) {
                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>");
                        flag = true;
                    }
                    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);
                    html.append("<td>").append(value == null ? "" : value).append("</td>");
                }
            }
        }
        html.append("\n</tr>");
        dealReplaceMapAddRowspan(replaceMap, needAddRowspanDataAreaGroupFieldNameList, recordFse, false);
        return html;
    }
    /**
     * èŽ·å–æ€»è®¡html
     *
     * @param statisticsMap               ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap      æ•°æ®åŒºå­—段缓存map
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param preFse                      æœ€è¿‘操作的数据fse
     * @param dataAreaGroupFieldRecordObj æ•°æ®åŒºåˆ†ç»„字段记录obj
     * @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);
        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 html.toString();
        }
        int index = 0;
        for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            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);
                html.append("<td>").append(value == null ? "" : value).append("</td>");
            } else {
                if (index == 1) {
                    html.append("<td colspan=\"").append(dataAreaGroupFieldNameList.size()).append("\">").append(totalName).append("</td>");
                }
            }
        }
        html.append("\n</tr>");
        return html.toString();
    }
    /**
     * å¤„理参数替换map-添加数据区分组字段的占行
     *
     * @param replaceMap                            æ›¿æ¢map
     * @param needAddRowspanDataAreaGroupFieldNames éœ€è¦æ·»åŠ å è¡Œçš„æ•°æ®åŒºåˆ†ç»„å­—æ®µåç§°é›†åˆ
     * @param recordFse                             ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void dealReplaceMapAddRowspan(Map<String, String> replaceMap, Collection<String> needAddRowspanDataAreaGroupFieldNames, FieldSetEntity recordFse, boolean commonFieldFlag) {
        JSONObject keyObj = new JSONObject();
        String value;
        int rowspan;
        String paramKey;
        int index = 0;
        for (String fieldName : needAddRowspanDataAreaGroupFieldNames) {
            index++;
            keyObj.put(fieldName, recordFse.getString(fieldName));
            if (commonFieldFlag && index != needAddRowspanDataAreaGroupFieldNames.size()) {
                continue;
            }
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(fieldName));
            paramKey = dataListReportService.concat(keyObj, CmnConst.ATTR_ROWSPAN);
            value = replaceMap.get(paramKey);
            rowspan = StringUtils.isEmpty(value) ? 1 : Integer.parseInt(value) + 1;
            replaceMap.put(paramKey, String.valueOf(rowspan));
        }
    }
    /**
     * è®°å½•分组字段
     *
     * @param dataAreaGroupFieldRecordObj æ•°æ®åŒºåˆ†ç»„字段记录obj
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param curFieldIndex               å½“前字段下标
     * @param recordFse                   ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void recordDataAreaGroupField(JSONObject dataAreaGroupFieldRecordObj, List<String> dataAreaGroupFieldNameList, int curFieldIndex, FieldSetEntity recordFse) {
        String tempFieldName;
        JSONObject parentObj = dataAreaGroupFieldRecordObj;
        JSONObject subObj;
        String value;
        for (int j = 0; j <= curFieldIndex; j++) {
            tempFieldName = dataAreaGroupFieldNameList.get(j);
            value = recordFse.getString(tempFieldName);
            subObj = parentObj.getJSONObject(value);
            if (subObj == null) {
                subObj = new JSONObject();
                parentObj.put(value, subObj);
            }
            parentObj = subObj;
        }
    }
    /**
     * èŽ·å–æŒ‡å®šçš„ä¸‹çº§åˆ†ç»„é¡¹
     *
     * @param keyObj                      æŒ‡å®šçš„key
     * @param dataAreaGroupFieldNameList  æ•°æ®åŒºåˆ†ç»„字段名称list
     * @param dataAreaGroupFieldRecordObj æ•°æ®åŒºåˆ†ç»„字段记录obj
     * @return
     */
    private int getAimSubCnt(JSONObject keyObj, List<String> dataAreaGroupFieldNameList, JSONObject dataAreaGroupFieldRecordObj) {
        String value;
        JSONObject parentObj = dataAreaGroupFieldRecordObj;
        JSONObject subObj = null;
        for (String fieldName : dataAreaGroupFieldNameList) {
            value = keyObj.getString(fieldName);
            if (value != null) {
                subObj = parentObj.getJSONObject(value);
            } else {
                break;
            }
            parentObj = subObj;
        }
        return subObj == null ? 0 : subObj.size();
    }
}
src/main/java/com/product/server/report/service/DataListReportService.java
@@ -20,6 +20,8 @@
import com.product.core.spring.context.SpringMVCContextHolder;
import com.product.server.report.config.CmnConst;
import com.product.server.report.config.ReportCode;
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;
@@ -249,6 +251,180 @@
        for (Map.Entry<Integer, List<JSONObject>> entry : reportConfigMap.entrySet()) {
            RedisUtil.setHash(reportConfigUUID, String.valueOf(entry.getKey()), entry.getValue());
        }
    }
    /**
     * èŽ·å–æŠ¥è¡¨
     *
     * @return
     */
    public ReportEntity getReportEntity(FieldSetEntity fse) {
        ReportEntity report = null;
        String reportConfigUUID = fse.getUUID();
        if (!StringUtils.isEmpty(fse.getString("~report_config_uuid~"))) {
            reportConfigUUID = fse.getString("~report_config_uuid~");
        }
        DataTableEntity reportConfigDte = DataPoolCacheImpl.getInstance().getCacheData(CmnConst.CACHE_REPORT_CONFIG, new String[]{reportConfigUUID});
        if (BaseUtil.dataTableIsEmpty(reportConfigDte)) {
            throw new BaseException(ReportCode.GET_CACHE_FIAL.getValue(), ReportCode.GET_CACHE_FIAL.getText() + ":" + CmnConst.CACHE_REPORT_CONFIG);
        }
        FieldSetEntity reportConfigFse = reportConfigDte.getFieldSetEntity(0);
        if (!"1".equals(reportConfigFse.getString(CmnConst.IS_VALID))) {
            throw new BaseException(ReportCode.INVALID_REPORT.getValue(), ReportCode.INVALID_REPORT.getText());
        }
        DataTableEntity reportTypeDte = DataPoolCacheImpl.getInstance().getCacheData(CmnConst.CACHE_REPORT_TYPE, new String[]{reportConfigFse.getString(CmnConst.TYPE_UUID)});
        if (BaseUtil.dataTableIsEmpty(reportTypeDte)) {
            throw new BaseException(ReportCode.GET_CACHE_FIAL.getValue(), ReportCode.GET_CACHE_FIAL.getText() + ":" + CmnConst.CACHE_REPORT_TYPE);
        }
        FieldSetEntity reportTypeFse = reportTypeDte.getFieldSetEntity(0);
        StringBuilder sort = new StringBuilder(128);
        // èŽ·å–æŠ¥è¡¨ç¼“å­˜ä¿¡æ¯
        Map<Integer, List<JSONObject>> reportConfigMap = getReportConfig(reportConfigUUID, sort);
        // æ ¹æ®å…·ä½“çš„function_uuid和button_uuid获取对应的路由,拼凑url
        String url;
        JSONObject valueObj;
        for (Map.Entry<Integer, List<JSONObject>> entry : reportConfigMap.entrySet()) {
            if (entry.getValue() != null) {
                for (JSONObject tempValueObj : entry.getValue()) {
                    if (entry.getValue() != null) {
                        for (Map.Entry<String, Object> innerEntry : tempValueObj.entrySet()) {
                            if (innerEntry.getValue() instanceof JSONObject) {
                                valueObj = (JSONObject) innerEntry.getValue();
                                if (!StringUtils.isEmpty(valueObj.getString(CmnConst.SUB_REPORT))) {
                                    url = valueObj.getString(CmnConst.SUB_REPORT);
                                    if (!StringUtils.isEmpty(valueObj.getString(CmnConst.ATTR_URL_PARAM))) {
                                        url += "?" + valueObj.getString(CmnConst.ATTR_URL_PARAM);
                                    }
                                    valueObj.put(CmnConst.ATTR_URL, url);
                                    valueObj.put("~isSubReport~", true);
                                } else {
                                    if (!StringUtils.isEmpty(valueObj.getString(CmnConst.FUNCTION_UUID)) && !StringUtils.isEmpty(valueObj.getString(CmnConst.BUTTON_UUID))) {
                                        url = routerService.functionSkipByButtonUuid(valueObj.getString(CmnConst.FUNCTION_UUID), valueObj.getString(CmnConst.BUTTON_UUID));
                                        if (StringUtils.isEmpty(url)) {
                                            valueObj.remove(CmnConst.ATTR_URL);
                                        } else {
                                            if (!StringUtils.isEmpty(valueObj.getString(CmnConst.ATTR_URL_PARAM))) {
                                                url += "?" + valueObj.getString(CmnConst.ATTR_URL_PARAM);
                                            }
                                            valueObj.put(CmnConst.ATTR_URL, url);
                                        }
                                    } else {
                                        valueObj.remove(CmnConst.ATTR_URL);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        // èŽ·å–æŠ¥è¡¨ç±»åž‹
        String reportType = reportTypeFse.getString(CmnConst.TYPE_NAME);
        // èŽ·å–æ•°æ®æº
        DataTableEntity reportSourceDte = DataPoolCacheImpl.getInstance().getCacheData(CmnConst.CACHE_REPORT_DATASOURCE_CONFIG, new String[]{reportConfigFse.getString(CmnConst.DATASOURCE_UUID)});
        if (BaseUtil.dataTableIsEmpty(reportSourceDte)) {
            throw new BaseException(ReportCode.GET_REPORT_DATASOURCE_FIAL.getValue(), ReportCode.GET_REPORT_DATASOURCE_FIAL.getText());
        }
        FieldSetEntity reportSourceFse = reportSourceDte.getFieldSetEntity(0);
        int curPage = fse.getInteger(CmnConst.CPAGE) == null ? 1 : fse.getInteger(CmnConst.CPAGE);
        JSONObject resultObj = new JSONObject();
        // é¦–次加载,默认添加条件
        Map<String, Map<String, String>> outerMap = Maps.newHashMap();
        if (fse.getBoolean(CmnConst.FIRST_LOAD)) {
            DataTableEntity allFilterDte = getDefaultSearchFilter(reportSourceFse.getUUID());
            Map<String, DataTableEntity> subMap = Maps.newHashMap();
            if (!DataTableEntity.isEmpty(allFilterDte)) {
                subMap.put("systemFieldMeta", allFilterDte);
            }
            DataTableEntity defaultFilterDte = new DataTableEntity();
            FieldSetEntity tempFse;
            Map<String, String> innerMap;
            for (int i = 0; i < allFilterDte.getRows(); i++) {
                tempFse = allFilterDte.getFieldSetEntity(i);
                if (StringUtils.isEmpty(tempFse.getString(CmnConst.LOGICAL_VALUE))) {
                    continue;
                }
                defaultFilterDte.addFieldSetEntity(tempFse);
                innerMap = Maps.newHashMap();
                innerMap.put(CmnConst.LOGICAL_OPERATOR, tempFse.getString(CmnConst.LOGICAL_OPERATOR));
                innerMap.put(CmnConst.LOGICAL_VALUE, tempFse.getString(CmnConst.LOGICAL_VALUE));
                innerMap.put(CmnConst.FIELD_TYPE, tempFse.getString(CmnConst.FIELD_TYPE));
                outerMap.put(tempFse.getString(CmnConst.FIELD_NAME), innerMap);
            }
//            resultObj.put("filterInfo", outerMap);
            if (!DataTableEntity.isEmpty(defaultFilterDte)) {
                subMap.put("systemSeniorQueryString", defaultFilterDte);
            }
            fse.setSubData(subMap);
        }
        DataTableEntity recordDte = getRecordDte(sort, curPage, reportSourceFse, fse, reportConfigFse, null);
        // æ˜¯å¦æ·»åŠ æ€»åˆè®¡
        boolean totalStatisticsFlag = "1".equalsIgnoreCase(reportConfigFse.getString(CmnConst.LAST_TOTAL));
        String totalName = totalStatisticsFlag ? (StringUtils.isEmpty(reportConfigFse.getString(CmnConst.TOTAL_NAME)) ? "总计" : reportConfigFse.getString(CmnConst.TOTAL_NAME)) : "";
//        JSONObject checkObj = checkRecordDte(recordDte);
//        if (CmnConst.FALSE.equals(checkObj.getString(CmnConst.RETURN_ATTR_RESULT))) {
//            return checkObj;
//        }
//        StringBuilder tableStyle = new StringBuilder(32);
//        String widthType = reportConfigFse.getString(CmnConst.REPORT_WIDTH_TYPE);
//        String width = reportConfigFse.getString(CmnConst.REPORT_WIDTH_VALUE);
//        if (!StringUtils.isEmpty(width)) {
//            if ("1".equals(widthType)) {
//                tableStyle.append(" style=\"width:").append(width).append("%\"");
//            } else if ("0".equals(widthType)) {
//                tableStyle.append(" style=\"width:").append(width).append("px\"");
//            }
//        }
        if (CmnConst.REPORT_TYPE_COMMON.equals(reportType)) {
            report = commonReportService.getReportEntity(recordDte, totalName, reportConfigMap);
            //TODO
            report.setFilterInfo(outerMap);
        } else if (CmnConst.REPORT_TYPE_GROUP.equals(reportType)) {
            // ç‰¹æ®Šå¤„理额外查询内容
            recordDte.addFieldSetEntity(getRecordDte(sort, curPage, reportSourceFse, fse, reportConfigFse, reportConfigMap));
//            resultObj.putAll(groupReportService.getReport(recordDte, totalName, reportConfigMap, tableStyle));
        }
        //TODO
//        report.setFilterInfo(outerMap);
        if (CmnConst.FALSE.equals(resultObj.getString(CmnConst.RETURN_ATTR_RESULT))) {
            resultObj.put(CmnConst.RETURN_ATTR_RESULT, true);
            resultObj.put(CmnConst.RETURN_ATTR_MESSAGE, "获取报表失败!");
        } else {
            // åˆ†é¡µå‚æ•°
            SQLEntity sqlEntity = recordDte.getSqle();
            if ("1".equals(reportConfigFse.getString(CmnConst.IS_PAGE)) && sqlEntity != null) {
                report.setPage(true);
                report.setTotalPage(sqlEntity.getTotalpage());
                report.setPageSize(StringUtils.isEmpty(reportConfigFse.getString(CmnConst.PAGE_SIZE)) ? 0 : reportConfigFse.getInteger(CmnConst.PAGE_SIZE));
                report.setPageIndex(curPage);
//                resultObj.put(CmnConst.IS_PAGE, 1);
//                resultObj.put(CmnConst.CPAGE, curPage);
//                resultObj.put("totalCount", sqlEntity.getTotalCount());
//                resultObj.put("totalpage", sqlEntity.getTotalpage());
//                resultObj.put("pagesize", StringUtils.isEmpty(reportConfigFse.getString(CmnConst.PAGE_SIZE)) ? 0 : reportConfigFse.getInteger(CmnConst.PAGE_SIZE));
            }
        }
//        if (!DataTableEntity.isEmpty(recordDte)) {
//            resultObj.put("current_page_count", recordDte.getRows());
//        }
        report.setSystemFieldMeta(getSearchInfo(reportSourceFse.getUUID()));
        report.setReportType(reportConfigFse.getString("type_uuid"));
//        resultObj.put("systemFieldMeta", );
        resultObj.put("report_type", reportConfigFse.getString("type_uuid"));
//        System.out.println(resultObj.getString("html"));
        return report;
    }
    /**
@@ -1017,6 +1193,74 @@
    }
    /**
     * æŠ¥è¡¨-解析-获取头部或者尾部标题Html
     *
     * @param list                    æŠ¥è¡¨é…ç½®ä¿¡æ¯ï¼Œç¼“å­˜list
     * @param totalColCount           æ€»åˆ—æ•°
     * @param headAndTailTitleDataMap å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºæ•°æ®å­—段map
     * @param locationType            ä½ç½®ç±»åž‹ï¼Œhead-头部,tail-尾部
     * @return
     */
    public List<List<ReportColumn>> getTitle(List<JSONObject> list, int totalColCount, Map<String, Set<String>> headAndTailTitleDataMap, String locationType) {
        List<List<ReportColumn>> row = new ArrayList<>();
        StringBuilder html = new StringBuilder(1024);
        int colspan;
        int preRow = 0;
        int preCol = 0;
        int curRow;
        int curCol;
        String value;
        String style;
        List<ReportColumn> reportColumns = new ArrayList<>();
        for (JSONObject singleObj : list) {
            curRow = singleObj.getIntValue(CmnConst.ATTR_Y);
            curCol = singleObj.getIntValue(CmnConst.ATTR_X);
            colspan = singleObj.getIntValue(CmnConst.ATTR_COLSPAN);
            colspan = colspan < 0 ? totalColCount : Math.max(1, colspan);
            value = replaceFormDataAndSysData(singleObj.getString(CmnConst.ATTR_SHOW_NAME), headAndTailTitleDataMap, singleObj);
            value = value == null ? "" : value;
            if ("1".equals(singleObj.getString(CmnConst.ATTR_IS_TITLE))) {
                style = " class=\"" + CmnConst.CLASS_TR_REPORT_TITLE + "\"";
            } else {
                if ("head".equals(locationType)) {
                    style = " class=\"" + CmnConst.CLASS_TR_HEAD + "\"";
                } else {
                    style = " class=\"" + CmnConst.CLASS_TR_TAIL + "\"";
                }
            }
            if (preRow < curRow) {
                if (preRow != 0) {
                    row.add(getAimNumTdPlaceholderColumn(totalColCount - preCol, 1));
                }
                if (preRow == 0 && preCol == 0 && preCol < curCol - 1) {
                    row.add(getAimNumTdPlaceholderColumn(curCol - preCol - 1, 1));
                }
                preCol = curCol;
            }
            if (preCol < curCol - 1) {
                row.add(getAimNumTdPlaceholderColumn(curCol - preCol - 1, 1));
            }
            if ("1".equals(singleObj.getString(CmnConst.ATTR_IS_TITLE))) {
                colspan = totalColCount;
            }
            ReportColumn column = new ReportColumn();
            column.setColspan(colspan);
            column.setContent(value);
            reportColumns.add(column);
            preRow = curRow;
            preCol = curCol + singleObj.getIntValue(CmnConst.ATTR_COLSPAN) - 1;
            if ("1".equals(singleObj.getString(CmnConst.ATTR_IS_TITLE))) {
                preCol = totalColCount;
            }
        }
        row.add(reportColumns);
        reportColumns.addAll(getAimNumTdPlaceholderColumn(totalColCount - preCol, 1));
//        row.add();
        return row;
    }
    /**
     * èŽ·å–æŒ‡å®šæ•°æ®é›†ä¸­åŒ…å«çš„æ•°æ®åŒºå­—æ®µé›†åˆ
     *
     * @param list æŒ‡å®šæ•°æ®é›†åˆ
@@ -1500,6 +1744,30 @@
     * @param num
     * @return
     */
    public List<ReportColumn> getAimNumTdPlaceholderColumn(int num, int type) {
        List<ReportColumn> columns = new ArrayList<>();
        if (num <= 0) {
            return columns;
        }
        if (type == 0) {
            for (int i = 0; i < num; i++) {
                columns.add(new ReportColumn());
            }
        } else {
            ReportColumn column = new ReportColumn();
            column.setColspan(num);
            columns.add(column);
        }
        return columns;
    }
    /**
     * èŽ·å–æŒ‡å®šä¸ªæ•°çš„td占位
     *
     * @param num
     * @return
     */
    public String getAimNumTdPlaceholder(int num, int type) {
        StringBuilder result = new StringBuilder(64);
        if (num <= 0) {
src/main/java/com/product/server/report/service/GroupReportService.java
@@ -10,6 +10,7 @@
import com.product.core.service.support.AbstractBaseService;
import com.product.core.spring.context.SpringMVCContextHolder;
import com.product.server.report.config.CmnConst;
import com.product.server.report.entity.ReportColumn;
import com.product.util.BaseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,993 +26,1374 @@
 */
@Component
public class GroupReportService extends AbstractBaseService {
    @Autowired
    private DataListReportService dataListReportService;
    @Autowired
    private DataListReportService dataListReportService;
    /**
     * æŠ¥è¡¨-解析
     * @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);
    /**
     * æŠ¥è¡¨-解析
     *
     * @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);
        // css
        StringBuilder cssHtml = dataListReportService.getCssHtml();
        // 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 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 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");
        }
        // åº•部标题区
        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>");
//        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;
    }
        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;
    }
    /**
     * æŠ¥è¡¨-解析-放入分组表头区和数据Html,返回列数
     * @param groupAndDataAreaHtml          html容器
     * @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) {
        // åˆ†ç»„表头区字段缓存map
        Map<String, JSONObject> groupAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(2));
        // æ•°æ®åŒºå­—段缓存map
        Map<String, JSONObject> dataAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(0));
    /**
     * æŠ¥è¡¨-解析
     *
     * @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);
        // åˆ†ç»„表头区分组统计字段名称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));
        });
        // css
        StringBuilder cssHtml = dataListReportService.getCssHtml();
        // æ•°æ®åŒºåˆ†ç»„统计字段名称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);
                }
            }
        });
        // åˆ†ç»„表头区和数据区
        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);
        // èŽ·å–åˆ†ç»„è¡¨å¤´ç»Ÿè®¡å­—æ®µå¯¹åº”çš„æ•°æ®åŒºå­—æ®µmap
        Map<String, List<String>> groupAreaStatisticsField2DataAreaFieldMap = getGroupAreaStatisticsField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap);
        // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæœ«è¡Œåˆ†ç»„å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µmap
        Map<String, List<String>> groupAreaLastStageField2DataAreaFieldMap = getGroupAreaLastStageField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap, groupAreaRangeObj);
        // å¤´éƒ¨æ ‡é¢˜åŒº
        StringBuilder headTitleHtml = null;
        if (!reportConfigMap.get(1).isEmpty()) {
            headTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(1), totalColCount, headAndTailTitleDataMap, "head");
        }
        // èŽ·å–æŒ‡å®šæ•°æ®é›†ä¸­åŒ…å«çš„æ•°æ®åŒºå­—æ®µé›†åˆ
        Set<String> headAndTailFieldSet = Sets.newHashSet();
        headAndTailFieldSet.addAll(dataListReportService.getDataFields(reportConfigMap.get(1)));
        headAndTailFieldSet.addAll(dataListReportService.getDataFields(reportConfigMap.get(3)));
        // åº•部标题区
        StringBuilder tailTitleHtml = null;
        if (!reportConfigMap.get(3).isEmpty()) {
            tailTitleHtml = dataListReportService.getTitleHtml(reportConfigMap.get(3), totalColCount, headAndTailTitleDataMap, "tail");
        }
        // ç»Ÿè®¡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());
        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>");
        FieldSetEntity recordFse;
        for (int i = 0;i < recordDte.getRows();i++) {
            recordFse = recordDte.getFieldSetEntity(i);
        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;
    }
            // èŽ·å–å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºæ•°æ®å­—æ®µ
            dataListReportService.getHeadAndTailTitleDataMap(headAndTailTitleDataMap, headAndTailFieldSet, recordFse);
    /**
     * æŠ¥è¡¨-解析-放入分组表头区和数据Html,返回列数
     *
     * @param groupAndDataAreaHtml    html容器
     * @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) {
        // åˆ†ç»„表头区字段缓存map
        Map<String, JSONObject> groupAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(2));
        // æ•°æ®åŒºå­—段缓存map
        Map<String, JSONObject> dataAreaFieldConfigMap = dataListReportService.groupAndDataJSONObject2Map(reportConfigMap.get(0));
            // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæ•°æ®obj
            getGroupAreaFieldRecordObj(groupAreaFieldRecordObj, groupAreaFieldConfigMap, recordFse, groupAreaStatisticsField2DataAreaFieldMap, groupAreaLastStageField2DataAreaFieldMap);
        // åˆ†ç»„表头区分组统计字段名称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));
        });
            // èŽ·å–æ•°æ®åŒºåˆ†ç»„å­—æ®µobj
            getDataAreaGroupFieldRecordObj(dataAreaFieldRecordObj, dataAreaFieldConfigMap, recordFse);
        // æ•°æ®åŒºåˆ†ç»„统计字段名称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);
                }
            }
        });
            // èŽ·å–æ•°æ®åŒºç‰¹æ®Šå­—æ®µçš„ç»Ÿè®¡
            statisticsDataAreaSpField(statisticsMap, dataAreaFieldConfigMap, dataAreaSpStatisticsFieldNameList, recordFse);
        // èŽ·å–åˆ†ç»„è¡¨å¤´ç»Ÿè®¡å­—æ®µå¯¹åº”çš„æ•°æ®åŒºå­—æ®µmap
        Map<String, List<String>> groupAreaStatisticsField2DataAreaFieldMap = getGroupAreaStatisticsField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap);
        // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæœ«è¡Œåˆ†ç»„å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µmap
        Map<String, List<String>> groupAreaLastStageField2DataAreaFieldMap = getGroupAreaLastStageField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap, groupAreaRangeObj);
            // èŽ·å–æ•°æ®åŒºæ™®é€šå­—æ®µçš„ç»Ÿè®¡
            statisticsDataAreaCommonField(statisticsMap, dataAreaFieldConfigMap, dataAreaCommonStatisticsFieldNameList, recordFse);
        }
        // èŽ·å–æŒ‡å®šæ•°æ®é›†ä¸­åŒ…å«çš„æ•°æ®åŒºå­—æ®µé›†åˆ
        Set<String> headAndTailFieldSet = Sets.newHashSet();
        headAndTailFieldSet.addAll(dataListReportService.getDataFields(reportConfigMap.get(1)));
        headAndTailFieldSet.addAll(dataListReportService.getDataFields(reportConfigMap.get(3)));
        // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºå’Œæ•°æ®åŒºæ€»åˆ—æ•°
        int totalColCount = getTotalColCount(groupAreaFieldRecordObj, dataAreaGroupFieldNameList.size(), dataAreaCommonStatisticsFieldNameList.size());
        // ç»Ÿè®¡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());
        // æ•°æ®æ ‡é¢˜list
        List<JSONObject> dataTitleList = Lists.newArrayList();
        // èŽ·å–æ ‡é¢˜html
        groupAndDataAreaHtml.append(getDataAreaTitleHtml(dataTitleList, groupAreaFieldRecordObj, dataAreaFieldConfigMap, groupAreaRangeObj, dataAreaCommonStatisticsFieldNameList));
        // èŽ·å–å†…å®¹html
        groupAndDataAreaHtml.append(getDataAreaDataHtml(dataTitleList, dataAreaFieldRecordObj, statisticsMap, totalName, dataAreaGroupFieldNameList, dataAreaFieldConfigMap));
        FieldSetEntity recordFse;
        for (int i = 0; i < recordDte.getRows(); i++) {
            recordFse = recordDte.getFieldSetEntity(i);
        return totalColCount;
    }
            // èŽ·å–å¤´éƒ¨ã€å°¾éƒ¨æ ‡é¢˜åŒºæ•°æ®å­—æ®µ
            dataListReportService.getHeadAndTailTitleDataMap(headAndTailTitleDataMap, headAndTailFieldSet, recordFse);
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´ç»Ÿè®¡å­—æ®µå¯¹åº”çš„æ•°æ®åŒºå­—æ®µmap
     * @param groupAreaFieldConfigMap       åˆ†ç»„表头区字段缓存map
     * @param dataAreaFieldConfigMap        æ•°æ®åŒºå­—段缓存map
     * @return                              åˆ†ç»„表头统计字段对应的数据区字段map
     */
    private Map<String,List<String>> getGroupAreaStatisticsField2DataAreaFieldMap(Map<String, JSONObject> groupAreaFieldConfigMap, Map<String, JSONObject> dataAreaFieldConfigMap) {
        Map<String, List<String>> resultMap = Maps.newLinkedHashMap();
        String groupAreaFieldName;
        JSONObject groupAreaFieldConfigObj;
        JSONObject dataAreaFieldConfigObj;
        for (Map.Entry<String, JSONObject> groupAreaFieldConfigEntry : groupAreaFieldConfigMap.entrySet()) {
            groupAreaFieldConfigObj = groupAreaFieldConfigEntry.getValue();
            if (!"1".equals(groupAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
                continue;
            }
            groupAreaFieldName = groupAreaFieldConfigEntry.getKey();
            for (Map.Entry<String, JSONObject> dataAreaFieldConfigEntry : dataAreaFieldConfigMap.entrySet()) {
                dataAreaFieldConfigObj = dataAreaFieldConfigEntry.getValue();
                if (dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD)).contains(groupAreaFieldName)) {
                    resultMap.computeIfAbsent(groupAreaFieldName, k -> Lists.newArrayList()).add(dataAreaFieldConfigEntry.getKey());
                }
            }
        }
        return resultMap;
    }
            // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæ•°æ®obj
            getGroupAreaFieldRecordObj(groupAreaFieldRecordObj, groupAreaFieldConfigMap, recordFse, groupAreaStatisticsField2DataAreaFieldMap, groupAreaLastStageField2DataAreaFieldMap);
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæœ«è¡Œåˆ†ç»„å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µmap
     * @param groupAreaFieldConfigMap       åˆ†ç»„表头区字段缓存map
     * @param dataAreaFieldConfigMap        æ•°æ®åŒºå­—段缓存map
     * @param groupAreaRangeObj             åˆ†ç»„表头区范围obj
     * @return                              åˆ†ç»„表头区末行分组字段对应数据区字段map
     */
    private Map<String,List<String>> getGroupAreaLastStageField2DataAreaFieldMap(Map<String, JSONObject> groupAreaFieldConfigMap, Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject groupAreaRangeObj) {
        Map<String, List<String>> resultMap = Maps.newLinkedHashMap();
        String groupFieldName;
        JSONObject groupAreaFieldConfigObj;
        JSONObject dataAreaFieldConfigObj;
        for (Map.Entry<String, JSONObject> groupAreaFieldConfigEntry : groupAreaFieldConfigMap.entrySet()) {
            groupAreaFieldConfigObj = groupAreaFieldConfigEntry.getValue();
            if (groupAreaFieldConfigObj.getIntValue(CmnConst.ATTR_Y) + Math.max(1, groupAreaFieldConfigObj.getIntValue(CmnConst.ATTR_ROWSPAN)) - 1 < groupAreaRangeObj.getIntValue(CmnConst.MAX_Y)) {
                continue;
            }
            groupFieldName = groupAreaFieldConfigEntry.getKey();
            for (Map.Entry<String, JSONObject> dataAreaFieldConfigEntry : dataAreaFieldConfigMap.entrySet()) {
                dataAreaFieldConfigObj = dataAreaFieldConfigEntry.getValue();
                if (dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD)).contains(groupFieldName)) {
                    resultMap.computeIfAbsent(groupFieldName, k -> Lists.newArrayList()).add(dataAreaFieldConfigEntry.getKey());
                }
            }
            // èŽ·å–æ•°æ®åŒºåˆ†ç»„å­—æ®µobj
            getDataAreaGroupFieldRecordObj(dataAreaFieldRecordObj, dataAreaFieldConfigMap, recordFse);
        }
        return resultMap;
    }
            // èŽ·å–æ•°æ®åŒºç‰¹æ®Šå­—æ®µçš„ç»Ÿè®¡
            statisticsDataAreaSpField(statisticsMap, dataAreaFieldConfigMap, dataAreaSpStatisticsFieldNameList, recordFse);
    /**
     * èŽ·å–æ ‡é¢˜-分组表头区数据obj
     * @param groupAreaFieldRecordObj                       åˆ†ç»„表头区字段记录obj
     * @param groupAreaFieldConfigMap                       åˆ†ç»„表头区字段缓存map
     * @param recordFse                                     ä¸šåŠ¡æ•°æ®è®°å½•
     * @param groupAreaStatisticsField2DataAreaFieldMap     åˆ†ç»„表头区统计字段对应数据区字段map
     * @param groupAreaLastStageField2DataAreaFieldMap      åˆ†ç»„表头区末级字段对应数据区字段map
     */
    private void getGroupAreaFieldRecordObj(JSONObject groupAreaFieldRecordObj, Map<String, JSONObject> groupAreaFieldConfigMap, FieldSetEntity recordFse,
                                            Map<String, List<String>> groupAreaStatisticsField2DataAreaFieldMap, Map<String, List<String>> groupAreaLastStageField2DataAreaFieldMap) {
        JSONObject parentObj = groupAreaFieldRecordObj;
        JSONObject subObj;
        String value;
        List<String> list;
        String groupAreaGroupFieldName;
        int i = 0;
        List<String> statisticsFieldList;
        String realValue;
        for (Map.Entry<String, JSONObject> entry : groupAreaFieldConfigMap.entrySet()) {
            list = Lists.newArrayList();
            list.addAll(dataListReportService.transfer2List(entry.getValue().get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD)));
            list.add(entry.getKey());
            for (;i < list.size();i++) {
                groupAreaGroupFieldName = list.get(i);
                realValue = dataListReportService.getRealValue(recordFse, groupAreaGroupFieldName);
                value = recordFse.getString(groupAreaGroupFieldName);
            // èŽ·å–æ•°æ®åŒºæ™®é€šå­—æ®µçš„ç»Ÿè®¡
            statisticsDataAreaCommonField(statisticsMap, dataAreaFieldConfigMap, dataAreaCommonStatisticsFieldNameList, recordFse);
        }
                if (parentObj.get(value) == null) {
                    subObj = new JSONObject(Maps.newTreeMap((o1, o2) -> dataListReportService.compare(o1, o2)));
                    parentObj.put(value, subObj);
                    // æ·»åŠ åˆ†ç»„è¡¨å¤´åŒºåˆ†ç»„ç»Ÿè®¡å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µ
                    if (groupAreaStatisticsField2DataAreaFieldMap.containsKey(groupAreaGroupFieldName)) {
                        parentObj.put(value + CmnConst.STATISTICS_NAME, groupAreaStatisticsField2DataAreaFieldMap.get(groupAreaGroupFieldName));
                        statisticsFieldList = dataListReportService.transfer2List(parentObj.get(CmnConst.ATTR_STATISTICS_FIELD));
                        if (statisticsFieldList.isEmpty()) {
                            parentObj.put(CmnConst.ATTR_STATISTICS_FIELD, statisticsFieldList);
                        }
                        if (!statisticsFieldList.contains(value + CmnConst.STATISTICS_NAME)) {
                            statisticsFieldList.add(value + CmnConst.STATISTICS_NAME);
                        }
                    }
                    // æ·»åŠ åˆ†ç»„è¡¨å¤´åŒºæœ«çº§å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µ
                    if (groupAreaLastStageField2DataAreaFieldMap.containsKey(groupAreaGroupFieldName)) {
                        parentObj.put(value, groupAreaLastStageField2DataAreaFieldMap.get(groupAreaGroupFieldName));
                    }
                    // æ·»åŠ field_info
                    if (StringUtils.isEmpty(parentObj.getString(CmnConst.ATTR_FIELD_INFO))) {
                        parentObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(groupAreaGroupFieldName));
                    }
                    if (!StringUtils.isEmpty(realValue)) {
                        subObj.put(CmnConst.ATTR_REAL_VALUE, realValue);
                    }
                } else {
                    if (!(parentObj.get(value) instanceof JSONObject)) {
                        continue;
                    }
                    subObj = parentObj.getJSONObject(value);
                }
                parentObj = subObj;
            }
        }
    }
        // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºå’Œæ•°æ®åŒºæ€»åˆ—æ•°
        int totalColCount = getTotalColCount(groupAreaFieldRecordObj, dataAreaGroupFieldNameList.size(), dataAreaCommonStatisticsFieldNameList.size());
    /**
     * èŽ·å–æ•°æ®åŒºåˆ†ç»„å­—æ®µobj
     * @param dataAreaFieldRecordObj    æ•°æ®åŒºå­—段记录obj
     * @param dataAreaFieldConfigMap    æ•°æ®åŒºå­—段缓存map
     * @param recordFse                 ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void getDataAreaGroupFieldRecordObj(JSONObject dataAreaFieldRecordObj, Map<String, JSONObject> dataAreaFieldConfigMap, FieldSetEntity recordFse) {
        JSONObject parentObj = dataAreaFieldRecordObj;
        JSONObject subObj;
        String value;
        List<String> list;
        String groupAreaGroupFieldName;
        int i = 0;
        String dataAreaFieldName;
        JSONObject dataAreaFieldConfigObj;
        List<String> statisticsFieldList;
        String realValue;
        for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            dataAreaFieldConfigObj = entry.getValue();
            if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
                break;
            }
            dataAreaFieldName = entry.getKey();
            realValue = dataListReportService.getRealValue(recordFse, dataAreaFieldName);
            list = Lists.newArrayList();
            list.addAll(dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_DATA_AREA_GROUP_FIELD)));
            list.add(dataAreaFieldName);
            for (;i < list.size();i++) {
                groupAreaGroupFieldName = list.get(i);
                value = recordFse.getString(groupAreaGroupFieldName);
                subObj = parentObj.getJSONObject(value);
                if (subObj == null) {
                    subObj = new JSONObject(Maps.newLinkedHashMap());
                    parentObj.put(value, subObj);
                }
                // æ·»åŠ æ•°æ®åŒºåˆ†ç»„ç»Ÿè®¡å­—æ®µ
                if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
                    parentObj.put(value + CmnConst.STATISTICS_NAME, new JSONObject());
                    statisticsFieldList = dataListReportService.transfer2List(parentObj.get(CmnConst.ATTR_STATISTICS_FIELD));
                    if (statisticsFieldList.isEmpty()) {
                        parentObj.put(CmnConst.ATTR_STATISTICS_FIELD, statisticsFieldList);
                    }
                    if (!statisticsFieldList.contains(value + CmnConst.STATISTICS_NAME)) {
                        statisticsFieldList.add(value + CmnConst.STATISTICS_NAME);
                    }
                }
                // æ·»åŠ field_info
                if (StringUtils.isEmpty(parentObj.getString(CmnConst.ATTR_FIELD_INFO))) {
                    parentObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(groupAreaGroupFieldName));
                }
                if (!StringUtils.isEmpty(realValue)) {
                    subObj.put(CmnConst.ATTR_REAL_VALUE, realValue);
                }
                parentObj = subObj;
            }
        }
    }
        // æ•°æ®æ ‡é¢˜list
        List<JSONObject> dataTitleList = Lists.newArrayList();
        // èŽ·å–æ ‡é¢˜html
        groupAndDataAreaHtml.append(getDataAreaTitleHtml(dataTitleList, groupAreaFieldRecordObj, dataAreaFieldConfigMap, groupAreaRangeObj, dataAreaCommonStatisticsFieldNameList));
        // èŽ·å–å†…å®¹html
        groupAndDataAreaHtml.append(getDataAreaDataHtml(dataTitleList, dataAreaFieldRecordObj, statisticsMap, totalName, dataAreaGroupFieldNameList, dataAreaFieldConfigMap));
    /**
     * èŽ·å–æ•°æ®åŒºç‰¹æ®Šå­—æ®µçš„ç»Ÿè®¡
     * @param statisticsMap                         ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap                æ•°æ®åŒºå­—段缓存map
     * @param dataAreaSpStatisticsFieldNameList     æ•°æ®åŒºç‰¹æ®Šç»Ÿè®¡å­—段名称list(分组表头区下统计字段)
     * @param recordFse                             ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void statisticsDataAreaSpField(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaSpStatisticsFieldNameList, FieldSetEntity recordFse) {
        JSONObject dataAreaFieldConfigObj;
        List<String> groupAreaGroupFieldNameList;
        List<String> dataAreaGroupFieldNameList;
        String value;
        JSONObject keyObj;
        JSONObject valueObj;
        JSONObject groupAreaKeyObj;
        JSONObject dataAreaKeyObj;
        String statisticsType;
        for (String dataAreaFieldName : dataAreaSpStatisticsFieldNameList) {
            keyObj = new JSONObject();
            groupAreaKeyObj = new JSONObject();
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
            dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataAreaFieldName);
            groupAreaGroupFieldNameList = dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD));
            dataAreaGroupFieldNameList = dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_DATA_AREA_GROUP_FIELD));
            value = dataListReportService.getValue(dataAreaFieldConfigObj, recordFse, dataAreaFieldName);
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            for (String groupAreaGroupFieldName : groupAreaGroupFieldNameList) {
                dataAreaKeyObj = new JSONObject();
                for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                    keyObj.remove(dataAreaGroupFieldName);
                }
                groupAreaKeyObj.put(groupAreaGroupFieldName, recordFse.getString(groupAreaGroupFieldName));
                dataListReportService.extendJSONObject(keyObj, groupAreaKeyObj, null);
                valueObj = statisticsMap.get(keyObj);
                valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, true);
                statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
                for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                    dataAreaKeyObj.put(dataAreaGroupFieldName, recordFse.getString(dataAreaGroupFieldName));
                    keyObj = dataListReportService.extendJSONObject(keyObj, dataAreaKeyObj, null);
                    valueObj = statisticsMap.get(keyObj);
                    valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, true);
                    statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
                }
            }
        }
    }
        return totalColCount;
    }
    /**
     * å–数据区普通字段的统计
     * @param statisticsMap                             ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap                    æ•°æ®åŒºå­—段缓存map
     * @param dataAreaCommonStatisticsFieldNameList     æ•°æ®åŒºæ™®é€šç»Ÿè®¡å­—段名称list
     * @param recordFse                                 ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void statisticsDataAreaCommonField(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaCommonStatisticsFieldNameList, FieldSetEntity recordFse) {
        JSONObject dataAreaFieldConfigObj;
        List<String> dataAreaGroupFieldNameList;
        String value;
        JSONObject dataAreaKeyObj = new JSONObject();
        JSONObject keyObj;
        JSONObject valueObj;
        String statisticsType;
        int i;
        for (String dataAreaFieldName : dataAreaCommonStatisticsFieldNameList) {
            i = 0;
            keyObj = new JSONObject();
            dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataAreaFieldName);
            dataAreaGroupFieldNameList = dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_DATA_AREA_GROUP_FIELD));
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            value = dataListReportService.getValue(dataAreaFieldConfigObj, recordFse, dataAreaFieldName);
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
            valueObj = statisticsMap.get(keyObj);
            valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, false);
            statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
            for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                i++;
                dataAreaKeyObj.put(dataAreaGroupFieldName, recordFse.getString(dataAreaGroupFieldName));
                keyObj = dataListReportService.extendJSONObject(keyObj, dataAreaKeyObj, null);
                valueObj = statisticsMap.get(keyObj);
                valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, i == dataAreaGroupFieldNameList.size());
                statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
            }
        }
    }
    /**
     * æŠ¥è¡¨-解析-放入分组表头区和数据,返回列数
     *
     * @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));
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºå’Œæ•°æ®åŒºæ€»åˆ—æ•°
     * @param groupAreaFieldRecordObj               åˆ†ç»„表头区字段记录obj
     * @param dataAreaGroupFieldCount               æ•°æ®åŒºåˆ†ç»„字段个数
     * @param dataAreaCommonStatisticsFieldCount    æ•°æ®åŒºæ™®é€šç»Ÿè®¡å­—段个数
     * @return                                      åˆ†ç»„表头区和数据区总列数
     */
    private int getTotalColCount(JSONObject groupAreaFieldRecordObj, int dataAreaGroupFieldCount, int dataAreaCommonStatisticsFieldCount) {
        int groupAreaColCount = getGroupAreaColCount(groupAreaFieldRecordObj, 0);
        return groupAreaColCount + dataAreaGroupFieldCount + dataAreaCommonStatisticsFieldCount;
    }
        // åˆ†ç»„表头区分组统计字段名称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));
        });
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæ€»åˆ—æ•°
     * @param curOperateObj         å½“前操作的obj
     * @param groupAreaColCount     åˆ†ç»„表头区列数
     * @return                      åˆ†ç»„表头区总列数
     */
    private int getGroupAreaColCount(JSONObject curOperateObj, int groupAreaColCount) {
        if (curOperateObj == null) {
            return groupAreaColCount;
        }
        String key;
        Object value;
        for (Map.Entry<String, Object> entry : curOperateObj.entrySet()) {
            key = entry.getKey();
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key)) {
                continue;
            }
            value = entry.getValue();
            if (value == null) {
                continue;
            }
            if (value instanceof List) {
                groupAreaColCount += ((List) value).size();
            } else if (value instanceof JSONObject) {
                groupAreaColCount = getGroupAreaColCount((JSONObject) value, groupAreaColCount);
            }
        }
        return groupAreaColCount;
    }
        // æ•°æ®åŒºåˆ†ç»„统计字段名称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);
                }
            }
        });
    /**
     * èŽ·å–æ ‡é¢˜html
     * @param dataTitleList                             æ•°æ®æ ‡é¢˜list
     * @param groupAreaFieldRecordObj                   åˆ†ç»„表头区字段记录obj
     * @param dataAreaFieldConfigMap                    æ•°æ®åŒºå­—段缓存map
     * @param groupAreaRangeObj                         åˆ†ç»„表头去范围obj
     * @param dataAreaCommonStatisticsFieldNameList     æ•°æ®åŒºæ™®é€šç»Ÿè®¡å­—段名称list
     * @return                                          æ•°æ®æ ‡é¢˜html
     */
    private StringBuilder getDataAreaTitleHtml(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();
        // èŽ·å–åˆ†ç»„è¡¨å¤´ç»Ÿè®¡å­—æ®µå¯¹åº”çš„æ•°æ®åŒºå­—æ®µmap
        Map<String, List<String>> groupAreaStatisticsField2DataAreaFieldMap = getGroupAreaStatisticsField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap);
        // èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæœ«è¡Œåˆ†ç»„å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µmap
        Map<String, List<String>> groupAreaLastStageField2DataAreaFieldMap = getGroupAreaLastStageField2DataAreaFieldMap(groupAreaFieldConfigMap, dataAreaFieldConfigMap, groupAreaRangeObj);
        String dataAreaFieldName;
        JSONObject dataAreaFieldConfigObj;
        JSONObject titleObj;
        for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            dataAreaFieldName = entry.getKey();
            dataAreaFieldConfigObj = entry.getValue();
            titleObj = new JSONObject();
        // èŽ·å–æŒ‡å®šæ•°æ®é›†ä¸­åŒ…å«çš„æ•°æ®åŒºå­—æ®µé›†åˆ
        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;
    }
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´ç»Ÿè®¡å­—æ®µå¯¹åº”çš„æ•°æ®åŒºå­—æ®µmap
     *
     * @param groupAreaFieldConfigMap åˆ†ç»„表头区字段缓存map
     * @param dataAreaFieldConfigMap  æ•°æ®åŒºå­—段缓存map
     * @return åˆ†ç»„表头统计字段对应的数据区字段map
     */
    private Map<String, List<String>> getGroupAreaStatisticsField2DataAreaFieldMap(Map<String, JSONObject> groupAreaFieldConfigMap, Map<String, JSONObject> dataAreaFieldConfigMap) {
        Map<String, List<String>> resultMap = Maps.newLinkedHashMap();
        String groupAreaFieldName;
        JSONObject groupAreaFieldConfigObj;
        JSONObject dataAreaFieldConfigObj;
        for (Map.Entry<String, JSONObject> groupAreaFieldConfigEntry : groupAreaFieldConfigMap.entrySet()) {
            groupAreaFieldConfigObj = groupAreaFieldConfigEntry.getValue();
            if (!"1".equals(groupAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
                continue;
            }
            groupAreaFieldName = groupAreaFieldConfigEntry.getKey();
            for (Map.Entry<String, JSONObject> dataAreaFieldConfigEntry : dataAreaFieldConfigMap.entrySet()) {
                dataAreaFieldConfigObj = dataAreaFieldConfigEntry.getValue();
                if (dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD)).contains(groupAreaFieldName)) {
                    resultMap.computeIfAbsent(groupAreaFieldName, k -> Lists.newArrayList()).add(dataAreaFieldConfigEntry.getKey());
                }
            }
        }
        return resultMap;
    }
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæœ«è¡Œåˆ†ç»„å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µmap
     *
     * @param groupAreaFieldConfigMap åˆ†ç»„表头区字段缓存map
     * @param dataAreaFieldConfigMap  æ•°æ®åŒºå­—段缓存map
     * @param groupAreaRangeObj       åˆ†ç»„表头区范围obj
     * @return åˆ†ç»„表头区末行分组字段对应数据区字段map
     */
    private Map<String, List<String>> getGroupAreaLastStageField2DataAreaFieldMap(Map<String, JSONObject> groupAreaFieldConfigMap, Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject groupAreaRangeObj) {
        Map<String, List<String>> resultMap = Maps.newLinkedHashMap();
        String groupFieldName;
        JSONObject groupAreaFieldConfigObj;
        JSONObject dataAreaFieldConfigObj;
        for (Map.Entry<String, JSONObject> groupAreaFieldConfigEntry : groupAreaFieldConfigMap.entrySet()) {
            groupAreaFieldConfigObj = groupAreaFieldConfigEntry.getValue();
            if (groupAreaFieldConfigObj.getIntValue(CmnConst.ATTR_Y) + Math.max(1, groupAreaFieldConfigObj.getIntValue(CmnConst.ATTR_ROWSPAN)) - 1 < groupAreaRangeObj.getIntValue(CmnConst.MAX_Y)) {
                continue;
            }
            groupFieldName = groupAreaFieldConfigEntry.getKey();
            for (Map.Entry<String, JSONObject> dataAreaFieldConfigEntry : dataAreaFieldConfigMap.entrySet()) {
                dataAreaFieldConfigObj = dataAreaFieldConfigEntry.getValue();
                if (dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD)).contains(groupFieldName)) {
                    resultMap.computeIfAbsent(groupFieldName, k -> Lists.newArrayList()).add(dataAreaFieldConfigEntry.getKey());
                }
            }
        }
        return resultMap;
    }
    /**
     * èŽ·å–æ ‡é¢˜-分组表头区数据obj
     *
     * @param groupAreaFieldRecordObj                   åˆ†ç»„表头区字段记录obj
     * @param groupAreaFieldConfigMap                   åˆ†ç»„表头区字段缓存map
     * @param recordFse                                 ä¸šåŠ¡æ•°æ®è®°å½•
     * @param groupAreaStatisticsField2DataAreaFieldMap åˆ†ç»„表头区统计字段对应数据区字段map
     * @param groupAreaLastStageField2DataAreaFieldMap  åˆ†ç»„表头区末级字段对应数据区字段map
     */
    private void getGroupAreaFieldRecordObj(JSONObject groupAreaFieldRecordObj, Map<String, JSONObject> groupAreaFieldConfigMap, FieldSetEntity recordFse,
                                            Map<String, List<String>> groupAreaStatisticsField2DataAreaFieldMap, Map<String, List<String>> groupAreaLastStageField2DataAreaFieldMap) {
        JSONObject parentObj = groupAreaFieldRecordObj;
        JSONObject subObj;
        String value;
        List<String> list;
        String groupAreaGroupFieldName;
        int i = 0;
        List<String> statisticsFieldList;
        String realValue;
        for (Map.Entry<String, JSONObject> entry : groupAreaFieldConfigMap.entrySet()) {
            list = Lists.newArrayList();
            list.addAll(dataListReportService.transfer2List(entry.getValue().get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD)));
            list.add(entry.getKey());
            for (; i < list.size(); i++) {
                groupAreaGroupFieldName = list.get(i);
                realValue = dataListReportService.getRealValue(recordFse, groupAreaGroupFieldName);
                value = recordFse.getString(groupAreaGroupFieldName);
                if (parentObj.get(value) == null) {
                    subObj = new JSONObject(Maps.newTreeMap((o1, o2) -> dataListReportService.compare(o1, o2)));
                    parentObj.put(value, subObj);
                    // æ·»åŠ åˆ†ç»„è¡¨å¤´åŒºåˆ†ç»„ç»Ÿè®¡å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µ
                    if (groupAreaStatisticsField2DataAreaFieldMap.containsKey(groupAreaGroupFieldName)) {
                        parentObj.put(value + CmnConst.STATISTICS_NAME, groupAreaStatisticsField2DataAreaFieldMap.get(groupAreaGroupFieldName));
                        statisticsFieldList = dataListReportService.transfer2List(parentObj.get(CmnConst.ATTR_STATISTICS_FIELD));
                        if (statisticsFieldList.isEmpty()) {
                            parentObj.put(CmnConst.ATTR_STATISTICS_FIELD, statisticsFieldList);
                        }
                        if (!statisticsFieldList.contains(value + CmnConst.STATISTICS_NAME)) {
                            statisticsFieldList.add(value + CmnConst.STATISTICS_NAME);
                        }
                    }
                    // æ·»åŠ åˆ†ç»„è¡¨å¤´åŒºæœ«çº§å­—æ®µå¯¹åº”æ•°æ®åŒºå­—æ®µ
                    if (groupAreaLastStageField2DataAreaFieldMap.containsKey(groupAreaGroupFieldName)) {
                        parentObj.put(value, groupAreaLastStageField2DataAreaFieldMap.get(groupAreaGroupFieldName));
                    }
                    // æ·»åŠ field_info
                    if (StringUtils.isEmpty(parentObj.getString(CmnConst.ATTR_FIELD_INFO))) {
                        parentObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(groupAreaGroupFieldName));
                    }
                    if (!StringUtils.isEmpty(realValue)) {
                        subObj.put(CmnConst.ATTR_REAL_VALUE, realValue);
                    }
                } else {
                    if (!(parentObj.get(value) instanceof JSONObject)) {
                        continue;
                    }
                    subObj = parentObj.getJSONObject(value);
                }
                parentObj = subObj;
            }
        }
    }
    /**
     * èŽ·å–æ•°æ®åŒºåˆ†ç»„å­—æ®µobj
     *
     * @param dataAreaFieldRecordObj æ•°æ®åŒºå­—段记录obj
     * @param dataAreaFieldConfigMap æ•°æ®åŒºå­—段缓存map
     * @param recordFse              ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void getDataAreaGroupFieldRecordObj(JSONObject dataAreaFieldRecordObj, Map<String, JSONObject> dataAreaFieldConfigMap, FieldSetEntity recordFse) {
        JSONObject parentObj = dataAreaFieldRecordObj;
        JSONObject subObj;
        String value;
        List<String> list;
        String groupAreaGroupFieldName;
        int i = 0;
        String dataAreaFieldName;
        JSONObject dataAreaFieldConfigObj;
        List<String> statisticsFieldList;
        String realValue;
        for (Map.Entry<String, JSONObject> entry : dataAreaFieldConfigMap.entrySet()) {
            dataAreaFieldConfigObj = entry.getValue();
            if (!"1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_GROUP))) {
                break;
            }
            dataAreaFieldName = entry.getKey();
            realValue = dataListReportService.getRealValue(recordFse, dataAreaFieldName);
            list = Lists.newArrayList();
            list.addAll(dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_DATA_AREA_GROUP_FIELD)));
            list.add(dataAreaFieldName);
            for (; i < list.size(); i++) {
                groupAreaGroupFieldName = list.get(i);
                value = recordFse.getString(groupAreaGroupFieldName);
                subObj = parentObj.getJSONObject(value);
                if (subObj == null) {
                    subObj = new JSONObject(Maps.newLinkedHashMap());
                    parentObj.put(value, subObj);
                }
                // æ·»åŠ æ•°æ®åŒºåˆ†ç»„ç»Ÿè®¡å­—æ®µ
                if ("1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_STATISTICS))) {
                    parentObj.put(value + CmnConst.STATISTICS_NAME, new JSONObject());
                    statisticsFieldList = dataListReportService.transfer2List(parentObj.get(CmnConst.ATTR_STATISTICS_FIELD));
                    if (statisticsFieldList.isEmpty()) {
                        parentObj.put(CmnConst.ATTR_STATISTICS_FIELD, statisticsFieldList);
                    }
                    if (!statisticsFieldList.contains(value + CmnConst.STATISTICS_NAME)) {
                        statisticsFieldList.add(value + CmnConst.STATISTICS_NAME);
                    }
                }
                // æ·»åŠ field_info
                if (StringUtils.isEmpty(parentObj.getString(CmnConst.ATTR_FIELD_INFO))) {
                    parentObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(groupAreaGroupFieldName));
                }
                if (!StringUtils.isEmpty(realValue)) {
                    subObj.put(CmnConst.ATTR_REAL_VALUE, realValue);
                }
                parentObj = subObj;
            }
        }
    }
    /**
     * èŽ·å–æ•°æ®åŒºç‰¹æ®Šå­—æ®µçš„ç»Ÿè®¡
     *
     * @param statisticsMap                     ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap            æ•°æ®åŒºå­—段缓存map
     * @param dataAreaSpStatisticsFieldNameList æ•°æ®åŒºç‰¹æ®Šç»Ÿè®¡å­—段名称list(分组表头区下统计字段)
     * @param recordFse                         ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void statisticsDataAreaSpField(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaSpStatisticsFieldNameList, FieldSetEntity recordFse) {
        JSONObject dataAreaFieldConfigObj;
        List<String> groupAreaGroupFieldNameList;
        List<String> dataAreaGroupFieldNameList;
        String value;
        JSONObject keyObj;
        JSONObject valueObj;
        JSONObject groupAreaKeyObj;
        JSONObject dataAreaKeyObj;
        String statisticsType;
        for (String dataAreaFieldName : dataAreaSpStatisticsFieldNameList) {
            keyObj = new JSONObject();
            groupAreaKeyObj = new JSONObject();
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
            dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataAreaFieldName);
            groupAreaGroupFieldNameList = dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_GROUP_AREA_GROUP_FIELD));
            dataAreaGroupFieldNameList = dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_DATA_AREA_GROUP_FIELD));
            value = dataListReportService.getValue(dataAreaFieldConfigObj, recordFse, dataAreaFieldName);
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            for (String groupAreaGroupFieldName : groupAreaGroupFieldNameList) {
                dataAreaKeyObj = new JSONObject();
                for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                    keyObj.remove(dataAreaGroupFieldName);
                }
                groupAreaKeyObj.put(groupAreaGroupFieldName, recordFse.getString(groupAreaGroupFieldName));
                dataListReportService.extendJSONObject(keyObj, groupAreaKeyObj, null);
                valueObj = statisticsMap.get(keyObj);
                valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, true);
                statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
                for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                    dataAreaKeyObj.put(dataAreaGroupFieldName, recordFse.getString(dataAreaGroupFieldName));
                    keyObj = dataListReportService.extendJSONObject(keyObj, dataAreaKeyObj, null);
                    valueObj = statisticsMap.get(keyObj);
                    valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, true);
                    statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
                }
            }
        }
    }
    /**
     * å–数据区普通字段的统计
     *
     * @param statisticsMap                         ç»Ÿè®¡map
     * @param dataAreaFieldConfigMap                æ•°æ®åŒºå­—段缓存map
     * @param dataAreaCommonStatisticsFieldNameList æ•°æ®åŒºæ™®é€šç»Ÿè®¡å­—段名称list
     * @param recordFse                             ä¸šåŠ¡æ•°æ®è®°å½•
     */
    private void statisticsDataAreaCommonField(Map<JSONObject, JSONObject> statisticsMap, Map<String, JSONObject> dataAreaFieldConfigMap, List<String> dataAreaCommonStatisticsFieldNameList, FieldSetEntity recordFse) {
        JSONObject dataAreaFieldConfigObj;
        List<String> dataAreaGroupFieldNameList;
        String value;
        JSONObject dataAreaKeyObj = new JSONObject();
        JSONObject keyObj;
        JSONObject valueObj;
        String statisticsType;
        int i;
        for (String dataAreaFieldName : dataAreaCommonStatisticsFieldNameList) {
            i = 0;
            keyObj = new JSONObject();
            dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataAreaFieldName);
            dataAreaGroupFieldNameList = dataListReportService.transfer2List(dataAreaFieldConfigObj.get(CmnConst.ATTR_DATA_AREA_GROUP_FIELD));
            statisticsType = dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS);
            value = dataListReportService.getValue(dataAreaFieldConfigObj, recordFse, dataAreaFieldName);
            keyObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
            valueObj = statisticsMap.get(keyObj);
            valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, false);
            statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
            for (String dataAreaGroupFieldName : dataAreaGroupFieldNameList) {
                i++;
                dataAreaKeyObj.put(dataAreaGroupFieldName, recordFse.getString(dataAreaGroupFieldName));
                keyObj = dataListReportService.extendJSONObject(keyObj, dataAreaKeyObj, null);
                valueObj = statisticsMap.get(keyObj);
                valueObj = dataListReportService.getStatisticsValueObj(valueObj, dataAreaFieldConfigObj, value, statisticsType, i == dataAreaGroupFieldNameList.size());
                statisticsMap.put((JSONObject) keyObj.clone(), valueObj);
            }
        }
    }
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºå’Œæ•°æ®åŒºæ€»åˆ—æ•°
     *
     * @param groupAreaFieldRecordObj            åˆ†ç»„表头区字段记录obj
     * @param dataAreaGroupFieldCount            æ•°æ®åŒºåˆ†ç»„字段个数
     * @param dataAreaCommonStatisticsFieldCount æ•°æ®åŒºæ™®é€šç»Ÿè®¡å­—段个数
     * @return åˆ†ç»„表头区和数据区总列数
     */
    private int getTotalColCount(JSONObject groupAreaFieldRecordObj, int dataAreaGroupFieldCount, int dataAreaCommonStatisticsFieldCount) {
        int groupAreaColCount = getGroupAreaColCount(groupAreaFieldRecordObj, 0);
        return groupAreaColCount + dataAreaGroupFieldCount + dataAreaCommonStatisticsFieldCount;
    }
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºæ€»åˆ—æ•°
     *
     * @param curOperateObj     å½“前操作的obj
     * @param groupAreaColCount åˆ†ç»„表头区列数
     * @return åˆ†ç»„表头区总列数
     */
    private int getGroupAreaColCount(JSONObject curOperateObj, int groupAreaColCount) {
        if (curOperateObj == null) {
            return groupAreaColCount;
        }
        String key;
        Object value;
        for (Map.Entry<String, Object> entry : curOperateObj.entrySet()) {
            key = entry.getKey();
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key)) {
                continue;
            }
            value = entry.getValue();
            if (value == null) {
                continue;
            }
            if (value instanceof List) {
                groupAreaColCount += ((List) value).size();
            } else if (value instanceof JSONObject) {
                groupAreaColCount = getGroupAreaColCount((JSONObject) value, groupAreaColCount);
            }
        }
        return groupAreaColCount;
    }
    /**
     * èŽ·å–æ ‡é¢˜
     *
     * @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);
                }
            }
        }
            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);
        dataTitleList.addAll(groupAreaRangeTitleList.get(maxRow));
        dataTitleList.addAll(afterGroupAreaTitleList);
        List<JSONObject> firstList = Lists.newArrayList();
        firstList.addAll(beforeGroupAreaTitleList);
        firstList.addAll(groupAreaRangeTitleList.get(0));
        firstList.addAll(afterGroupAreaTitleList);
        List<JSONObject> firstList = Lists.newArrayList();
        firstList.addAll(beforeGroupAreaTitleList);
        firstList.addAll(groupAreaRangeTitleList.get(0));
        firstList.addAll(afterGroupAreaTitleList);
        groupAreaRangeTitleList.set(0, firstList);
        groupAreaRangeTitleList.set(0, firstList);
        StringBuilder html = new StringBuilder(1024);
        int rowspan;
        int colspan;
        String value;
        for (List<JSONObject> rowList : groupAreaRangeTitleList) {
            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);
                }
                html.append("<td rowspan=\"").append(rowspan).append("\" colspan=\"").append(colspan).append("\">").append(value == null ? "" : value).append("</td>");
            }
            html.append("\n</tr>");
        }
        return html;
    }
//        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;
    }
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºèŒƒå›´çš„æ ‡é¢˜list
     * @param dataAreaFieldConfigMap        æ•°æ®åŒºå­—段缓存map
     * @param curOperateObj                 å½“前操作obj
     * @param groupAreaRangeTitleList       åˆ†ç»„表头区范围下数据标题list
     * @param preTitleObj                   æœ€è¿‘操作的标题obj
     * @param row                           å½“前行数
     * @param maxRow                        æœ€å¤§è¡Œæ•°
     */
    private void getGroupAreaRangeTitleList(Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject curOperateObj, List<List<JSONObject>> groupAreaRangeTitleList, JSONObject preTitleObj, int row, int maxRow, String parentFieldName) {
        if (curOperateObj == null || curOperateObj.isEmpty()) {
            return;
        }
        String key;
        Object value;
        JSONObject curTitleObj;
        String groupAreaFieldName = dataListReportService.fieldInfo2FieldName(curOperateObj.getString(CmnConst.ATTR_FIELD_INFO));
        List<String> statisticsFieldList = dataListReportService.transfer2List(curOperateObj.get(CmnConst.ATTR_STATISTICS_FIELD));
        String realValue = curOperateObj.getString(CmnConst.ATTR_REAL_VALUE);
        String fieldName = dataListReportService.fieldInfo2FieldName(curOperateObj.getString(CmnConst.ATTR_FIELD_INFO));
        String newKey;
        JSONObject realValueObj;
        for (Map.Entry<String, Object> entry : curOperateObj.entrySet()) {
            curTitleObj = dataListReportService.extendJSONObject(null, preTitleObj, null);
            if (!StringUtils.isEmpty(realValue) && !StringUtils.isEmpty(parentFieldName)) {
                if (curTitleObj.get(CmnConst.ATTR_REAL_VALUE) == null) {
                    realValueObj = new JSONObject();
                    curTitleObj.put(CmnConst.ATTR_REAL_VALUE, realValueObj);
                } else {
                    realValueObj = (JSONObject) curTitleObj.get(CmnConst.ATTR_REAL_VALUE);
                }
                realValueObj.put(parentFieldName, realValue);
            }
            key = entry.getKey();
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key) || CmnConst.ATTR_REAL_VALUE.equals(key)) {
                continue;
            }
            value = entry.getValue();
            if (value == null) {
                continue;
            }
            curTitleObj.put(CmnConst.ATTR_SHOW_NAME, key);
            if (statisticsFieldList.contains(key)) {
                curTitleObj.put(CmnConst.ATTR_IS_STATISTICS_FIELD, "1");
                newKey = key.replace(CmnConst.STATISTICS_NAME, "");
                curTitleObj.put(groupAreaFieldName, newKey);
                curTitleObj.put(CmnConst.ATTR_GROUP_AREA_SUB_CNT, getSubCnt(curOperateObj.getJSONObject(newKey)));
            } else {
                curTitleObj.put(groupAreaFieldName, key);
            }
            groupAreaRangeTitleList.get(row).add(curTitleObj);
            if (value instanceof List) {
                curTitleObj.put(CmnConst.ATTR_COLSPAN, ((List) value).size());
                curTitleObj.put(CmnConst.ATTR_ROWSPAN, maxRow - row);
                for (String dataAreaFieldName : (List<String>) value) {
                    JSONObject cloneTitleObj = (JSONObject) curTitleObj.clone();
                    cloneTitleObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
                    cloneTitleObj.put(CmnConst.ATTR_COLSPAN, 1);
                    cloneTitleObj.put(CmnConst.ATTR_ROWSPAN, 1);
                    JSONObject dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataAreaFieldName);
                    cloneTitleObj.put(CmnConst.ATTR_SHOW_NAME, dataListReportService.getStatisticsDesc(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS), dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME)));
                    groupAreaRangeTitleList.get(maxRow).add(cloneTitleObj);
                }
            } else {
                if (value instanceof JSONObject) {
                    curTitleObj.put(CmnConst.ATTR_COLSPAN, getGroupAreaColCount((JSONObject) value, 0));
                    curTitleObj.put(CmnConst.ATTR_ROWSPAN, 1);
                    getGroupAreaRangeTitleList(dataAreaFieldConfigMap, (JSONObject) value, groupAreaRangeTitleList, curTitleObj, row + 1, maxRow, fieldName);
                }
            }
        }
    }
    /**
     * èŽ·å–æ ‡é¢˜html
     *
     * @param dataTitleList                         æ•°æ®æ ‡é¢˜list
     * @param groupAreaFieldRecordObj               åˆ†ç»„表头区字段记录obj
     * @param dataAreaFieldConfigMap                æ•°æ®åŒºå­—段缓存map
     * @param groupAreaRangeObj                     åˆ†ç»„表头去范围obj
     * @param dataAreaCommonStatisticsFieldNameList æ•°æ®åŒºæ™®é€šç»Ÿè®¡å­—段名称list
     * @return æ•°æ®æ ‡é¢˜html
     */
    private StringBuilder getDataAreaTitleHtml(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);
    /**
     * èŽ·å–ä¸‹çº§åˆ†ç»„é¡¹æ•°
     * @param obj       å¾…操作的obj
     * @return          ä¸‹çº§åˆ†ç»„项数
     */
    private int getSubCnt(JSONObject obj) {
        int count = 0;
        List<String> statisticsFieldList = dataListReportService.transfer2List(obj.get(CmnConst.ATTR_STATISTICS_FIELD));
        for (String key : obj.keySet()) {
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key) || statisticsFieldList.contains(key)) {
                continue;
            }
            count++;
        }
        return count;
    }
        List<JSONObject> beforeGroupAreaTitleList = Lists.newArrayList();
        List<JSONObject> afterGroupAreaTitleList = Lists.newArrayList();
    /**
     * èŽ·å–å†…å®¹html
     * @param dataTitleList                 æ•°æ®æ ‡é¢˜list
     * @param dataAreaFieldRecordObj        æ•°æ®åŒºå­—段记录obj
     * @param statisticsMap                 ç»Ÿè®¡map
     * @param totalName                     æ€»è®¡åç§°ï¼Œéžç©º-需要总计
     * @param dataAreaGroupFieldNameList    æ•°æ®åŒºåˆ†ç»„字段名称list
     * @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) {
        List<List<JSONObject>> dataAreaGroupFieldDataList = Lists.newArrayList();
        getDataAreaGroupFieldDataList(dataAreaFieldRecordObj, dataAreaGroupFieldDataList, new JSONObject(), 1, 1, dataAreaGroupFieldNameList.size(), null);
        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);
                }
            }
        }
        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);
        }
        dataTitleList.addAll(groupAreaRangeTitleList.get(maxRow));
        dataTitleList.addAll(afterGroupAreaTitleList);
        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++;
                }
            } else {
                html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
            }
            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>");
        }
        List<JSONObject> firstList = Lists.newArrayList();
        firstList.addAll(beforeGroupAreaTitleList);
        firstList.addAll(groupAreaRangeTitleList.get(0));
        firstList.addAll(afterGroupAreaTitleList);
        // æ€»è®¡
        if (!StringUtils.isEmpty(totalName)) {
            html.append(getTotalStatisticsHtml(statisticsMap, dataTitleList, dataAreaFieldConfigMap, dataAreaGroupFieldNameList.size(), getSubCnt(dataAreaFieldRecordObj), totalName));
        }
        groupAreaRangeTitleList.set(0, firstList);
        return html;
    }
        StringBuilder html = new StringBuilder(1024);
        int rowspan;
        int colspan;
        String value;
        for (List<JSONObject> rowList : groupAreaRangeTitleList) {
            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);
                }
                html.append("<td rowspan=\"").append(rowspan).append("\" colspan=\"").append(colspan).append("\">").append(value == null ? "" : value).append("</td>");
            }
            html.append("\n</tr>");
        }
        return html;
    }
    /**
     * èŽ·å–æ•°æ®åŒºåˆ†ç»„å­—æ®µæ•°æ®list
     * @param curOperateObj                 å½“前操作obj
     * @param dataAreaGroupFieldDataList    æ•°æ®åŒºåˆ†ç»„字段数据list
     * @param preObj                        æœ€è¿‘操作的obj
     * @param row                           å½“前行数
     * @param maxCol                        æœ€å¤§åˆ—æ•°
     * @return                              æ•°æ®åŒºåˆ†ç»„字段数据list
     */
    private int getDataAreaGroupFieldDataList(JSONObject curOperateObj, List<List<JSONObject>> dataAreaGroupFieldDataList, JSONObject preObj, int row, int col, int maxCol, String parentFieldName) {
        String key;
        Object value;
        JSONObject curObj;
        JSONObject topLeftCornerObj;
        String dataAreaFieldName = dataListReportService.fieldInfo2FieldName(curOperateObj.getString(CmnConst.ATTR_FIELD_INFO));
        List<String> statisticsFieldList = dataListReportService.transfer2List(curOperateObj.get(CmnConst.ATTR_STATISTICS_FIELD));
        String realValue = curOperateObj.getString(CmnConst.ATTR_REAL_VALUE);
        JSONObject objValue;
        String newKey;
        List<JSONObject> curRowList;
        JSONObject realValueObj;
        for (Map.Entry<String, Object> entry : curOperateObj.entrySet()) {
            curObj = dataListReportService.extendJSONObject(null, preObj, null);
            key = entry.getKey();
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key) || CmnConst.ATTR_REAL_VALUE.equals(key)) {
                continue;
            }
            value = entry.getValue();
            if (value == null) {
                continue;
            }
            if (!StringUtils.isEmpty(realValue) && !StringUtils.isEmpty(parentFieldName)) {
                if (curObj.get(CmnConst.ATTR_REAL_VALUE) == null) {
                    realValueObj = new JSONObject();
                    curObj.put(CmnConst.ATTR_REAL_VALUE, realValueObj);
                } else {
                    realValueObj = (JSONObject) curObj.get(CmnConst.ATTR_REAL_VALUE);
                }
                realValueObj.put(parentFieldName, realValue);
            }
            if (dataAreaGroupFieldDataList.size() < row) {
                curRowList = Lists.newArrayList();
                dataAreaGroupFieldDataList.add(curRowList);
            } else {
                curRowList = dataAreaGroupFieldDataList.get(row - 1);
            }
            if (curRowList.size() < col - 1) {
                for (int i = 0; i < col - 1; i++) {
                    curRowList.add(new JSONObject());
                }
            }
            curObj.put(CmnConst.ATTR_SHOW_NAME, key);
            if (statisticsFieldList.contains(key)) {
                curObj.put(CmnConst.ATTR_IS_STATISTICS_FIELD, "1");
                newKey = key.replace(CmnConst.STATISTICS_NAME, "");
                curObj.put(dataAreaFieldName, newKey);
                curObj.put(CmnConst.ATTR_DATA_AREA_SUB_CNT, getSubCnt(curOperateObj.getJSONObject(newKey)));
            } else {
                curObj.put(dataAreaFieldName, key);
            }
            curObj.put(CmnConst.ATTR_ROWSPAN, 1);
            curObj.put(CmnConst.ATTR_COLSPAN, 1);
            curRowList.add(curObj);
            if (value instanceof JSONObject) {
                objValue = (JSONObject) value;
                if (objValue.isEmpty()) {
                    curObj.put(CmnConst.ATTR_COLSPAN, maxCol - col + 1);
                    if (row > 1 && col > 1) {
                        int emptyCount = 0;
                        for (JSONObject obj : curRowList) {
                            if (obj.isEmpty()) {
                                emptyCount++;
                            } else {
                                break;
                            }
                        }
                        for (int j = 0; j < emptyCount; j++) {
                            int curRow = row - 2;
                            topLeftCornerObj = dataAreaGroupFieldDataList.get(curRow).get(j);
                            while (topLeftCornerObj.isEmpty() && curRow > 0) {
                                curRow--;
                                topLeftCornerObj = dataAreaGroupFieldDataList.get(curRow).get(j);
                            }
                            topLeftCornerObj.put(CmnConst.ATTR_ROWSPAN, topLeftCornerObj.getIntValue(CmnConst.ATTR_ROWSPAN) + 1);
                        }
                    }
                    row++;
                    continue;
                }
                if (objValue.size() == 1&& objValue.containsKey(CmnConst.ATTR_REAL_VALUE)) {
                    row++;
                    continue;
                }
                row = getDataAreaGroupFieldDataList(objValue, dataAreaGroupFieldDataList, curObj, row, col + 1, maxCol, dataAreaFieldName);
            }
        }
        return row;
    }
    /**
     * èŽ·å–åˆ†ç»„è¡¨å¤´åŒºèŒƒå›´çš„æ ‡é¢˜list
     *
     * @param dataAreaFieldConfigMap  æ•°æ®åŒºå­—段缓存map
     * @param curOperateObj           å½“前操作obj
     * @param groupAreaRangeTitleList åˆ†ç»„表头区范围下数据标题list
     * @param preTitleObj             æœ€è¿‘操作的标题obj
     * @param row                     å½“前行数
     * @param maxRow                  æœ€å¤§è¡Œæ•°
     */
    private void getGroupAreaRangeTitleList(Map<String, JSONObject> dataAreaFieldConfigMap, JSONObject curOperateObj, List<List<JSONObject>> groupAreaRangeTitleList, JSONObject preTitleObj, int row, int maxRow, String parentFieldName) {
        if (curOperateObj == null || curOperateObj.isEmpty()) {
            return;
        }
        String key;
        Object value;
        JSONObject curTitleObj;
        String groupAreaFieldName = dataListReportService.fieldInfo2FieldName(curOperateObj.getString(CmnConst.ATTR_FIELD_INFO));
        List<String> statisticsFieldList = dataListReportService.transfer2List(curOperateObj.get(CmnConst.ATTR_STATISTICS_FIELD));
        String realValue = curOperateObj.getString(CmnConst.ATTR_REAL_VALUE);
        String fieldName = dataListReportService.fieldInfo2FieldName(curOperateObj.getString(CmnConst.ATTR_FIELD_INFO));
        String newKey;
        JSONObject realValueObj;
        for (Map.Entry<String, Object> entry : curOperateObj.entrySet()) {
            curTitleObj = dataListReportService.extendJSONObject(null, preTitleObj, null);
            if (!StringUtils.isEmpty(realValue) && !StringUtils.isEmpty(parentFieldName)) {
                if (curTitleObj.get(CmnConst.ATTR_REAL_VALUE) == null) {
                    realValueObj = new JSONObject();
                    curTitleObj.put(CmnConst.ATTR_REAL_VALUE, realValueObj);
                } else {
                    realValueObj = (JSONObject) curTitleObj.get(CmnConst.ATTR_REAL_VALUE);
                }
                realValueObj.put(parentFieldName, realValue);
            }
            key = entry.getKey();
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key) || CmnConst.ATTR_REAL_VALUE.equals(key)) {
                continue;
            }
            value = entry.getValue();
            if (value == null) {
                continue;
            }
            curTitleObj.put(CmnConst.ATTR_SHOW_NAME, key);
            if (statisticsFieldList.contains(key)) {
                curTitleObj.put(CmnConst.ATTR_IS_STATISTICS_FIELD, "1");
                newKey = key.replace(CmnConst.STATISTICS_NAME, "");
                curTitleObj.put(groupAreaFieldName, newKey);
                curTitleObj.put(CmnConst.ATTR_GROUP_AREA_SUB_CNT, getSubCnt(curOperateObj.getJSONObject(newKey)));
            } else {
                curTitleObj.put(groupAreaFieldName, key);
            }
            groupAreaRangeTitleList.get(row).add(curTitleObj);
            if (value instanceof List) {
                curTitleObj.put(CmnConst.ATTR_COLSPAN, ((List) value).size());
                curTitleObj.put(CmnConst.ATTR_ROWSPAN, maxRow - row);
                for (String dataAreaFieldName : (List<String>) value) {
                    JSONObject cloneTitleObj = (JSONObject) curTitleObj.clone();
                    cloneTitleObj.put(CmnConst.ATTR_FIELD_INFO, dataListReportService.fieldName2FieldInfo(dataAreaFieldName));
                    cloneTitleObj.put(CmnConst.ATTR_COLSPAN, 1);
                    cloneTitleObj.put(CmnConst.ATTR_ROWSPAN, 1);
                    JSONObject dataAreaFieldConfigObj = dataAreaFieldConfigMap.get(dataAreaFieldName);
                    cloneTitleObj.put(CmnConst.ATTR_SHOW_NAME, dataListReportService.getStatisticsDesc(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS), dataAreaFieldConfigObj.getString(CmnConst.ATTR_SHOW_NAME)));
                    groupAreaRangeTitleList.get(maxRow).add(cloneTitleObj);
                }
            } else {
                if (value instanceof JSONObject) {
                    curTitleObj.put(CmnConst.ATTR_COLSPAN, getGroupAreaColCount((JSONObject) value, 0));
                    curTitleObj.put(CmnConst.ATTR_ROWSPAN, 1);
                    getGroupAreaRangeTitleList(dataAreaFieldConfigMap, (JSONObject) value, groupAreaRangeTitleList, curTitleObj, row + 1, maxRow, fieldName);
                }
            }
        }
    }
    /**
     * èŽ·å–æ€»è®¡html
     * @param statisticsMap             ç»Ÿè®¡map
     * @param dataTitleList             æ•°æ®æ ‡é¢˜list
     * @param dataAreaFieldConfigMap    æ•°æ®åŒºå­—段缓存map
     * @param dataAreaGroupFieldCount   æ•°æ®åŒºåˆ†ç»„字段个数
     * @param dataAreaSubCnt            æ•°æ®åŒºä¸‹çº§åˆ†ç»„项数
     * @param totalName                 æ€»è®¡åç§°
     * @return
     */
    private StringBuilder getTotalStatisticsHtml(Map<JSONObject, JSONObject> statisticsMap, List<JSONObject> dataTitleList, Map<String, JSONObject> dataAreaFieldConfigMap, int dataAreaGroupFieldCount, int dataAreaSubCnt, String totalName) {
        StringBuilder html = new StringBuilder(256);
        html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
        html.append("<td colspan=\"").append(dataAreaGroupFieldCount).append("\">").append(totalName).append("</td>");
        JSONObject dataTitleObj;
        JSONObject keyObj;
        JSONObject dataAreaFieldConfigObj;
        String statisticsType;
        JSONObject valueObj;
        String value;
        int groupAreaSubCnt;
        for (int i = 0;i < dataTitleList.size();i++) {
            dataTitleObj = dataTitleList.get(i);
            keyObj = dataListReportService.extendJSONObject(null, 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);
                keyObj.remove(CmnConst.ATTR_GROUP_AREA_SUB_CNT);
                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))) {
                        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, dataTitleObj);
                html.append(dataListReportService.getTdAttrByObj(keyObj));
                // æ ¼å¼
                value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            }
            html.append(">").append(value == null ? "" : value).append("</td>");
        }
        html.append("\n</tr>");
        return html;
    }
    /**
     * èŽ·å–ä¸‹çº§åˆ†ç»„é¡¹æ•°
     *
     * @param obj å¾…操作的obj
     * @return ä¸‹çº§åˆ†ç»„项数
     */
    private int getSubCnt(JSONObject obj) {
        int count = 0;
        List<String> statisticsFieldList = dataListReportService.transfer2List(obj.get(CmnConst.ATTR_STATISTICS_FIELD));
        for (String key : obj.keySet()) {
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key) || statisticsFieldList.contains(key)) {
                continue;
            }
            count++;
        }
        return count;
    }
    /**
     * èŽ·å–ç»Ÿè®¡æ–¹å¼ä¸ºâ€œæœ€ç»ˆç»Ÿè®¡â€çš„å€¼
     * @param keyObj
     * @param dataAreaFieldConfigObj
     * @param statisticsMap
     * @return
     */
    private String getFinalValue(JSONObject keyObj, JSONObject dataAreaFieldConfigObj, Map<JSONObject, JSONObject> statisticsMap) {
        JSONObject valueObj;
        String value;
        if (CmnConst.ATTR_STATISTICS_FINAL_STATISTICS.equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS))
                && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD)) && !StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA))) {
            // ç»Ÿè®¡æ–¹å¼ä¸ºâ€œæœ€ç»ˆç»Ÿè®¡â€
            String formula = dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA);
            List<String> suitList = dataListReportService.getSuitContent(formula, "\\{#((?!\\{).)+#\\}");
            JSONObject tempObj = (JSONObject) keyObj.clone();
            for (String suitContent : suitList) {
                tempObj.put(CmnConst.ATTR_FIELD_INFO, suitContent);
                valueObj = statisticsMap.get(tempObj);
                formula = formula.replace(suitContent, (valueObj == null || valueObj.getString(CmnConst.ATTR_FINAL_VALUE) == null ? "0" : valueObj.getString(CmnConst.ATTR_FINAL_VALUE)) + "d");
            }
            try {
                value = BaseUtil.executeExpression(formula.replace("/0", "/1").replaceAll("\\/\\{#[\\w]+#}", "/1").replaceAll("\\{#[\\w]+#}", "0"), Maps.newHashMap()).toString();
            } catch (Exception e) {
                SpringMVCContextHolder.getSystemLogger().error(e);
                value = "";
            }
        } else {
            value = "";
        }
        return value;
    }
    /**
     * èŽ·å–å†…å®¹html
     *
     * @param dataTitleList              æ•°æ®æ ‡é¢˜list
     * @param dataAreaFieldRecordObj     æ•°æ®åŒºå­—段记录obj
     * @param statisticsMap              ç»Ÿè®¡map
     * @param totalName                  æ€»è®¡åç§°ï¼Œéžç©º-需要总计
     * @param dataAreaGroupFieldNameList æ•°æ®åŒºåˆ†ç»„字段名称list
     * @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) {
        List<List<JSONObject>> dataAreaGroupFieldDataList = Lists.newArrayList();
        getDataAreaGroupFieldDataList(dataAreaFieldRecordObj, dataAreaGroupFieldDataList, new JSONObject(), 1, 1, dataAreaGroupFieldNameList.size(), null);
    /**
     * èŽ·å–è¡Œåˆ—ä¸¤ç§ä¸åŒçš„ä¸‹çº§åˆ†ç»„å¹³å‡å€¼
     * @param dataAreaSubCnt            æŒ‰ç…§æ•°æ®åŒºåˆ†ç»„的下级分组项数
     * @param groupAreaSubCnt           æŒ‰ç…§åˆ†ç»„表头去分组的下级分组项数
     * @param curFieldStatisticsObj     å½“前字段统计obj
     * @param dataAreaFieldConfigObj    å½“前数据区字段缓存obj
     * @param statisticsType            ç»Ÿè®¡ç±»åž‹
     * @return                          æŒ‰ç…§ä¸‹çº§åˆ†ç»„项进行平均的行列平均值
     */
    private String getTwoTypesGroupAvgStatisticsValue(int dataAreaSubCnt, int groupAreaSubCnt, JSONObject curFieldStatisticsObj, JSONObject dataAreaFieldConfigObj, String statisticsType) {
        // åˆ—下级分组
        StringBuilder value = new StringBuilder(32);
        String dataAreaValue = getGroupAreaGroupAvgStatisticsValue(dataAreaSubCnt, curFieldStatisticsObj, dataAreaFieldConfigObj, statisticsType, CmnConst.ATTR_STATISTICS_DATA_AREA_AVG, dataAreaSubCnt == 0);
        String groupAreaValue = getGroupAreaGroupAvgStatisticsValue(groupAreaSubCnt, curFieldStatisticsObj, dataAreaFieldConfigObj, statisticsType, CmnConst.ATTR_STATISTICS_GROUP_AREA_AVG, groupAreaSubCnt == 0);
        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);
        }
        if (dataAreaSubCnt > 0) {
            value.append(dataAreaValue);
        }
        if (dataAreaSubCnt > 0 && groupAreaSubCnt > 0) {
            value.append("\\");
        }
        if (groupAreaSubCnt > 0) {
            value.append(groupAreaValue);
        }
        return value.toString();
    }
        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++;
                }
            } else {
                html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
            }
            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>");
        }
    /**
     * èŽ·å–æŒ‡å®šç±»åž‹çš„ä¸‹çº§åˆ†ç»„å¹³å‡å€¼
     * @param subCnt                    ä¸‹çº§åˆ†ç»„项数
     * @param curFieldStatisticsObj     å½“前字段统计obj
     * @param dataAreaFieldConfigObj    å½“前数据区字段缓存obj
     * @param statisticsType            ç»Ÿè®¡ç±»åž‹
     * @param avgType                   CmnConst.ATTR_STATISTICS_DATA_AREA_AVG-行;CmnConst.ATTR_STATISTICS_GROUP_AREA_AVG-列
     * @return                          æŒ‰ç…§ä¸‹çº§åˆ†ç»„项进行平均的平均值
     */
    private String getGroupAreaGroupAvgStatisticsValue(int subCnt, JSONObject curFieldStatisticsObj, JSONObject dataAreaFieldConfigObj, String statisticsType, String avgType, boolean lastStage) {
        String value;
        if (subCnt > 0) {
            curFieldStatisticsObj.put(CmnConst.ATTR_STATISTICS_SUB_CNT, subCnt);
            dataListReportService.getAvgValue(curFieldStatisticsObj, dataAreaFieldConfigObj, lastStage);
            value = curFieldStatisticsObj.getString(statisticsType);
            curFieldStatisticsObj.put(avgType, value);
        } else {
            value = curFieldStatisticsObj.getString(statisticsType);
        }
        return value == null ? "" : value;
    }
        // æ€»è®¡
        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++;
                }
            } else {
                html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_COMMON).append("\">\n    ");
            }
            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>");
        }
        // æ€»è®¡
        if (!StringUtils.isEmpty(totalName)) {
            html.append(getTotalStatisticsHtml(statisticsMap, dataTitleList, dataAreaFieldConfigMap, dataAreaGroupFieldNameList.size(), getSubCnt(dataAreaFieldRecordObj), totalName));
        }
        return null;
    }
    /**
     * èŽ·å–æ•°æ®åŒºåˆ†ç»„å­—æ®µæ•°æ®list
     *
     * @param curOperateObj              å½“前操作obj
     * @param dataAreaGroupFieldDataList æ•°æ®åŒºåˆ†ç»„字段数据list
     * @param preObj                     æœ€è¿‘操作的obj
     * @param row                        å½“前行数
     * @param maxCol                     æœ€å¤§åˆ—æ•°
     * @return æ•°æ®åŒºåˆ†ç»„字段数据list
     */
    private int getDataAreaGroupFieldDataList(JSONObject curOperateObj, List<List<JSONObject>> dataAreaGroupFieldDataList, JSONObject preObj, int row, int col, int maxCol, String parentFieldName) {
        String key;
        Object value;
        JSONObject curObj;
        JSONObject topLeftCornerObj;
        String dataAreaFieldName = dataListReportService.fieldInfo2FieldName(curOperateObj.getString(CmnConst.ATTR_FIELD_INFO));
        List<String> statisticsFieldList = dataListReportService.transfer2List(curOperateObj.get(CmnConst.ATTR_STATISTICS_FIELD));
        String realValue = curOperateObj.getString(CmnConst.ATTR_REAL_VALUE);
        JSONObject objValue;
        String newKey;
        List<JSONObject> curRowList;
        JSONObject realValueObj;
        for (Map.Entry<String, Object> entry : curOperateObj.entrySet()) {
            curObj = dataListReportService.extendJSONObject(null, preObj, null);
            key = entry.getKey();
            if (CmnConst.ATTR_FIELD_INFO.equals(key) || CmnConst.ATTR_STATISTICS_FIELD.equals(key) || CmnConst.ATTR_REAL_VALUE.equals(key)) {
                continue;
            }
            value = entry.getValue();
            if (value == null) {
                continue;
            }
            if (!StringUtils.isEmpty(realValue) && !StringUtils.isEmpty(parentFieldName)) {
                if (curObj.get(CmnConst.ATTR_REAL_VALUE) == null) {
                    realValueObj = new JSONObject();
                    curObj.put(CmnConst.ATTR_REAL_VALUE, realValueObj);
                } else {
                    realValueObj = (JSONObject) curObj.get(CmnConst.ATTR_REAL_VALUE);
                }
                realValueObj.put(parentFieldName, realValue);
            }
            if (dataAreaGroupFieldDataList.size() < row) {
                curRowList = Lists.newArrayList();
                dataAreaGroupFieldDataList.add(curRowList);
            } else {
                curRowList = dataAreaGroupFieldDataList.get(row - 1);
            }
            if (curRowList.size() < col - 1) {
                for (int i = 0; i < col - 1; i++) {
                    curRowList.add(new JSONObject());
                }
            }
            curObj.put(CmnConst.ATTR_SHOW_NAME, key);
            if (statisticsFieldList.contains(key)) {
                curObj.put(CmnConst.ATTR_IS_STATISTICS_FIELD, "1");
                newKey = key.replace(CmnConst.STATISTICS_NAME, "");
                curObj.put(dataAreaFieldName, newKey);
                curObj.put(CmnConst.ATTR_DATA_AREA_SUB_CNT, getSubCnt(curOperateObj.getJSONObject(newKey)));
            } else {
                curObj.put(dataAreaFieldName, key);
            }
            curObj.put(CmnConst.ATTR_ROWSPAN, 1);
            curObj.put(CmnConst.ATTR_COLSPAN, 1);
            curRowList.add(curObj);
            if (value instanceof JSONObject) {
                objValue = (JSONObject) value;
                if (objValue.isEmpty()) {
                    curObj.put(CmnConst.ATTR_COLSPAN, maxCol - col + 1);
                    if (row > 1 && col > 1) {
                        int emptyCount = 0;
                        for (JSONObject obj : curRowList) {
                            if (obj.isEmpty()) {
                                emptyCount++;
                            } else {
                                break;
                            }
                        }
                        for (int j = 0; j < emptyCount; j++) {
                            int curRow = row - 2;
                            topLeftCornerObj = dataAreaGroupFieldDataList.get(curRow).get(j);
                            while (topLeftCornerObj.isEmpty() && curRow > 0) {
                                curRow--;
                                topLeftCornerObj = dataAreaGroupFieldDataList.get(curRow).get(j);
                            }
                            topLeftCornerObj.put(CmnConst.ATTR_ROWSPAN, topLeftCornerObj.getIntValue(CmnConst.ATTR_ROWSPAN) + 1);
                        }
                    }
                    row++;
                    continue;
                }
                if (objValue.size() == 1 && objValue.containsKey(CmnConst.ATTR_REAL_VALUE)) {
                    row++;
                    continue;
                }
                row = getDataAreaGroupFieldDataList(objValue, dataAreaGroupFieldDataList, curObj, row, col + 1, maxCol, dataAreaFieldName);
            }
        }
        return row;
    }
    /**
     * èŽ·å–æ€»è®¡html
     *
     * @param statisticsMap           ç»Ÿè®¡map
     * @param dataTitleList           æ•°æ®æ ‡é¢˜list
     * @param dataAreaFieldConfigMap  æ•°æ®åŒºå­—段缓存map
     * @param dataAreaGroupFieldCount æ•°æ®åŒºåˆ†ç»„字段个数
     * @param dataAreaSubCnt          æ•°æ®åŒºä¸‹çº§åˆ†ç»„项数
     * @param totalName               æ€»è®¡åç§°
     * @return
     */
    private StringBuilder getTotalStatisticsHtml(Map<JSONObject, JSONObject> statisticsMap, List<JSONObject> dataTitleList, Map<String, JSONObject> dataAreaFieldConfigMap, int dataAreaGroupFieldCount, int dataAreaSubCnt, String totalName) {
        StringBuilder html = new StringBuilder(256);
        html.append("\n<tr class=\"").append(CmnConst.CLASS_TR_DATA_STATISTICS).append("\">\n    ");
        html.append("<td colspan=\"").append(dataAreaGroupFieldCount).append("\">").append(totalName).append("</td>");
        JSONObject dataTitleObj;
        JSONObject keyObj;
        JSONObject dataAreaFieldConfigObj;
        String statisticsType;
        JSONObject valueObj;
        String value;
        int groupAreaSubCnt;
        for (int i = 0; i < dataTitleList.size(); i++) {
            dataTitleObj = dataTitleList.get(i);
            keyObj = dataListReportService.extendJSONObject(null, 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);
                keyObj.remove(CmnConst.ATTR_GROUP_AREA_SUB_CNT);
                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))) {
                        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, dataTitleObj);
                html.append(dataListReportService.getTdAttrByObj(keyObj));
                // æ ¼å¼
                value = dataListReportService.formatValue(dataAreaFieldConfigObj, value);
            }
            html.append(">").append(value == null ? "" : value).append("</td>");
        }
        html.append("\n</tr>");
        return html;
    }
    /**
     * èŽ·å–ç»Ÿè®¡æ–¹å¼ä¸ºâ€œæœ€ç»ˆç»Ÿè®¡â€çš„å€¼
     *
     * @param keyObj
     * @param dataAreaFieldConfigObj
     * @param statisticsMap
     * @return
     */
    private String getFinalValue(JSONObject keyObj, JSONObject dataAreaFieldConfigObj, Map<JSONObject, JSONObject> statisticsMap) {
        JSONObject valueObj;
        String value;
        if (CmnConst.ATTR_STATISTICS_FINAL_STATISTICS.equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_STATISTICS))
                && "1".equals(dataAreaFieldConfigObj.getString(CmnConst.ATTR_IS_CUSTOM_FIELD)) && !StringUtils.isEmpty(dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA))) {
            // ç»Ÿè®¡æ–¹å¼ä¸ºâ€œæœ€ç»ˆç»Ÿè®¡â€
            String formula = dataAreaFieldConfigObj.getString(CmnConst.ATTR_FORMULA);
            List<String> suitList = dataListReportService.getSuitContent(formula, "\\{#((?!\\{).)+#\\}");
            JSONObject tempObj = (JSONObject) keyObj.clone();
            for (String suitContent : suitList) {
                tempObj.put(CmnConst.ATTR_FIELD_INFO, suitContent);
                valueObj = statisticsMap.get(tempObj);
                formula = formula.replace(suitContent, (valueObj == null || valueObj.getString(CmnConst.ATTR_FINAL_VALUE) == null ? "0" : valueObj.getString(CmnConst.ATTR_FINAL_VALUE)) + "d");
            }
            try {
                value = BaseUtil.executeExpression(formula.replace("/0", "/1").replaceAll("\\/\\{#[\\w]+#}", "/1").replaceAll("\\{#[\\w]+#}", "0"), Maps.newHashMap()).toString();
            } catch (Exception e) {
                SpringMVCContextHolder.getSystemLogger().error(e);
                value = "";
            }
        } else {
            value = "";
        }
        return value;
    }
    /**
     * èŽ·å–è¡Œåˆ—ä¸¤ç§ä¸åŒçš„ä¸‹çº§åˆ†ç»„å¹³å‡å€¼
     *
     * @param dataAreaSubCnt         æŒ‰ç…§æ•°æ®åŒºåˆ†ç»„的下级分组项数
     * @param groupAreaSubCnt        æŒ‰ç…§åˆ†ç»„表头去分组的下级分组项数
     * @param curFieldStatisticsObj  å½“前字段统计obj
     * @param dataAreaFieldConfigObj å½“前数据区字段缓存obj
     * @param statisticsType         ç»Ÿè®¡ç±»åž‹
     * @return æŒ‰ç…§ä¸‹çº§åˆ†ç»„项进行平均的行列平均值
     */
    private String getTwoTypesGroupAvgStatisticsValue(int dataAreaSubCnt, int groupAreaSubCnt, JSONObject curFieldStatisticsObj, JSONObject dataAreaFieldConfigObj, String statisticsType) {
        // åˆ—下级分组
        StringBuilder value = new StringBuilder(32);
        String dataAreaValue = getGroupAreaGroupAvgStatisticsValue(dataAreaSubCnt, curFieldStatisticsObj, dataAreaFieldConfigObj, statisticsType, CmnConst.ATTR_STATISTICS_DATA_AREA_AVG, dataAreaSubCnt == 0);
        String groupAreaValue = getGroupAreaGroupAvgStatisticsValue(groupAreaSubCnt, curFieldStatisticsObj, dataAreaFieldConfigObj, statisticsType, CmnConst.ATTR_STATISTICS_GROUP_AREA_AVG, groupAreaSubCnt == 0);
        if (dataAreaSubCnt > 0) {
            value.append(dataAreaValue);
        }
        if (dataAreaSubCnt > 0 && groupAreaSubCnt > 0) {
            value.append("\\");
        }
        if (groupAreaSubCnt > 0) {
            value.append(groupAreaValue);
        }
        return value.toString();
    }
    /**
     * èŽ·å–æŒ‡å®šç±»åž‹çš„ä¸‹çº§åˆ†ç»„å¹³å‡å€¼
     *
     * @param subCnt                 ä¸‹çº§åˆ†ç»„项数
     * @param curFieldStatisticsObj  å½“前字段统计obj
     * @param dataAreaFieldConfigObj å½“前数据区字段缓存obj
     * @param statisticsType         ç»Ÿè®¡ç±»åž‹
     * @param avgType                CmnConst.ATTR_STATISTICS_DATA_AREA_AVG-行;CmnConst.ATTR_STATISTICS_GROUP_AREA_AVG-列
     * @return æŒ‰ç…§ä¸‹çº§åˆ†ç»„项进行平均的平均值
     */
    private String getGroupAreaGroupAvgStatisticsValue(int subCnt, JSONObject curFieldStatisticsObj, JSONObject dataAreaFieldConfigObj, String statisticsType, String avgType, boolean lastStage) {
        String value;
        if (subCnt > 0) {
            curFieldStatisticsObj.put(CmnConst.ATTR_STATISTICS_SUB_CNT, subCnt);
            dataListReportService.getAvgValue(curFieldStatisticsObj, dataAreaFieldConfigObj, lastStage);
            value = curFieldStatisticsObj.getString(statisticsType);
            curFieldStatisticsObj.put(avgType, value);
        } else {
            value = curFieldStatisticsObj.getString(statisticsType);
        }
        return value == null ? "" : value;
    }
}
src/main/java/com/product/server/report/service/ReportConfigService.java
@@ -317,7 +317,7 @@
        fseFunction.setValue(CmnConst.FUNCTION_NAME, fseReport.getString(CmnConst.REPORT_NAME));    //功能名称
        fseFunction.setValue(CmnConst.FUNCTION_DESCRIPTION, fseReport.getString(CmnConst.REPORT_NAME));    //功能描述
        fseFunction.setValue(CmnConst.STATUS_UUID, 1);            //是否启用
        fseFunction.setValue(CmnConst.FUNCTION_TYPE_UUID, 1);    //功能类型-业务功能
        fseFunction.setValue(CmnConst.FUNCTION_TYPE_UUID, 5);    //功能类型-业务后台管理 ç®¡ç†å‘˜å’Œæ™®é€šç”¨æˆ·éƒ½èƒ½åˆ†é…è¯¥æŠ¥è¡¨çš„æƒé™
        fseFunction.setValue(CmnConst.CLIENT_TYPE_UUID, "Web");        //客户端类型
        fseFunction.setValue(CmnConst.VERSION_UUID, "001");
        fseFunction.setValue(CmnConst.DATA_TYPE, 1);// æ•°æ®ç±»åž‹
@@ -384,11 +384,11 @@
        // æå–发布的角色相关数据
        List<String> roleUUIDList = Lists.newArrayList();
        List<String> clientUUIDList = Lists.newArrayList();
        String[] singleRoleInfoArr;
        clientUUIDList.add(SpringMVCContextHolder.getCurrentUser().getClientUuid());
//        String[] singleRoleInfoArr;
        for (String singleRoleInfo : fse.getString(CmnConst.ROLE_UUID).split("#")) {
            singleRoleInfoArr = singleRoleInfo.split(",");
            roleUUIDList.add(singleRoleInfoArr[2]);
            clientUUIDList.add(singleRoleInfoArr[0]);
//            singleRoleInfoArr = singleRoleInfo.split(",");
            roleUUIDList.add(singleRoleInfo);
        }
        // 2.绑定角色(给某一角色赋予该功能)