zoukankan      html  css  js  c++  java
  • 统计

    公司产品迭代,要重新设计统计这一块,分配到我这来做,现在基本已经完成,遂把其中的一部分,统计表的生成的方式拿出来做为总结,由于是一部分,这里我就剔除了业务方面的代码,只描述生成的过程。

      1 package com.sun.stat.basic;
      2 
      3 import java.io.Serializable;
      4 import java.util.ArrayList;
      5 import java.util.List;
      6 
      7 /**
      8  * 表头单元格对象
      9  * 
     10  */
     11 public class HeadCell implements Serializable, Cloneable {
     12 
     13     private static final long serialVersionUID = -1756261752878954234L;
     14 
     15     private String id;
     16     private String name;
     17     private HeadCell parent;// 父单元格对象
     18     private List<HeadCell> children = new ArrayList<HeadCell>();// 子单元格集合
     19     private Integer deep = 0;// 层深度
     20 
     21     public HeadCell(String id, String name, HeadCell parent, Integer deep) {
     22         super();
     23         this.id = id;
     24         this.name = name;
     25         this.parent = parent;
     26         this.deep = deep;
     27     }
     28 
     29     public HeadCell getParent() {
     30         return parent;
     31     }
     32 
     33     public void setParent(HeadCell parent) {
     34         this.parent = parent;
     35     }
     36 
     37     public List<HeadCell> getChildren() {
     38         return children;
     39     }
     40 
     41     public void setChildren(List<HeadCell> children) {
     42         this.children = children;
     43     }
     44 
     45     public String getId() {
     46         return id;
     47     }
     48 
     49     public void setId(String id) {
     50         this.id = id;
     51     }
     52 
     53     public Integer getDeep() {
     54         return deep;
     55     }
     56 
     57     public void setDeep(Integer deep) {
     58         this.deep = deep;
     59     }
     60 
     61     /*
     62      * 克隆headCell实体
     63      * 
     64      * @see java.lang.Object#clone()
     65      */
     66     @Override
     67     public HeadCell clone() {
     68         HeadCell o = null;
     69         try {
     70             o = (HeadCell) super.clone();
     71         } catch (CloneNotSupportedException e) {
     72             e.printStackTrace();
     73         }
     74         return o;
     75     }
     76 
     77     public String getName() {
     78         return name;
     79     }
     80 
     81     public void setName(String name) {
     82         this.name = name;
     83     }
     84 
     85     /**
     86      * 如果选择了多个统计项单元格对象的最底层的子对象的个数
     87      * 
     88      * @param countItem 统计项的个数
     89      * @return
     90      */
     91     public int getColNum(int countItem) {
     92         int rowNum = children.size();
     93         for (HeadCell hc : children) {
     94             if (hc.getColNum(countItem) > 0) {
     95                 rowNum += hc.getColNum(countItem) - 1;
     96             }
     97         }
     98         return rowNum == 0 ? countItem : rowNum;
     99     }
    100 
    101     /**
    102      * 取得单元格对象的最底层的子对象的个数
    103      * 
    104      * @return 深度
    105      */
    106     public int getRowNum() {
    107         int rowNum = children.size();
    108         for (HeadCell hc : children) {
    109             if (hc.getRowNum() > 0) {
    110                 rowNum += hc.getRowNum() - 1;
    111             }
    112         }
    113         return rowNum;
    114     }
    115 }

    在实际的产品应用中,报表这一块一直是一个产品的亮点,通常一个复杂的报表涉及到了字段的统计,数据范围的统计,多区域的统计,这就需要一条极为复杂的sql了
    ,这是最考验开发人员严谨性的时候,一个不小心大概就会“弄丢”了某些数据,当然我们这里就不讨论数据的生成了(我所用到的数据是通过实体间的关联关系,“字典”等生产的,说简单点就是我的sql是hibernate给写的,当然了,这是有局限性的,所以我们把sql报表单独抽离出来,但是最终的实现都是殊途同归)。

    上面的这个对象就是我们定义的单元格对象,一个统计表涉及到了横向表头、纵向表头,每个表头都可以有自己的子查询(子集),这就构建了一套拥有主子关系的对象,通过这些对象进一步生成统计表!

      1 package com.sun.stat.process;
      2 
      3 import java.util.ArrayList;
      4 import java.util.Collections;
      5 import java.util.List;
      6 import java.util.Map;
      7 import java.util.Map.Entry;
      8 import java.util.UUID;
      9 
     10 import org.junit.Test;
     11 
     12 import com.sun.stat.basic.HeadCell;
     13 import com.sun.stat.component.Th;
     14 import com.sun.stat.utils.ListUtils;
     15 
     16 /**
     17  * 通过HeadCell对象集合构建一个复杂的table
     18  */
     19 public class ProductTable {
     20     List<HeadCell> rowHeadCells = new ArrayList<HeadCell>();
     21     List<HeadCell> colHeadCells = new ArrayList<HeadCell>();
     22 
     23     /**
     24      * 准备数据,构造一系列的headCell对象
     25      */
     26     public void prepare() {
     27         // 新建一个横向表头
     28         HeadCell r1 = new HeadCell(UUID.randomUUID().toString(), "r1", null, 0);
     29         HeadCell r2 = new HeadCell(UUID.randomUUID().toString(), "r2", null, 0);
     30         HeadCell r3 = new HeadCell(UUID.randomUUID().toString(), "r3", null, 0);
     31         HeadCell r4 = new HeadCell(UUID.randomUUID().toString(), "r4", null, 0);
     32         HeadCell r5 = new HeadCell(UUID.randomUUID().toString(), "r5", null, 0);
     33         HeadCell r1_1 = new HeadCell(UUID.randomUUID().toString(), "r1_1", r1, 1);
     34         HeadCell r1_2 = new HeadCell(UUID.randomUUID().toString(), "r1_2", r1, 1);
     35         HeadCell r2_1 = new HeadCell(UUID.randomUUID().toString(), "r2_1", r2, 1);
     36         HeadCell r2_2 = new HeadCell(UUID.randomUUID().toString(), "r2_2", r2, 1);
     37         HeadCell r3_1 = new HeadCell(UUID.randomUUID().toString(), "r3_1", r3, 1);
     38         HeadCell r3_2 = new HeadCell(UUID.randomUUID().toString(), "r3_2", r3, 1);
     39         HeadCell r4_1 = new HeadCell(UUID.randomUUID().toString(), "r4_1", r4, 1);
     40         HeadCell r4_2 = new HeadCell(UUID.randomUUID().toString(), "r4_2", r4, 1);
     41         HeadCell r5_1 = new HeadCell(UUID.randomUUID().toString(), "r5_1", r5, 1);
     42         HeadCell r5_2 = new HeadCell(UUID.randomUUID().toString(), "r5_2", r5, 1);
     43         r1.getChildren().add(r1_1);
     44         r1.getChildren().add(r1_2);
     45         r2.getChildren().add(r2_1);
     46         r2.getChildren().add(r2_2);
     47         r3.getChildren().add(r3_1);
     48         r3.getChildren().add(r3_2);
     49         r4.getChildren().add(r4_1);
     50         r4.getChildren().add(r4_2);
     51         r5.getChildren().add(r5_1);
     52         r5.getChildren().add(r5_2);
     53 
     54         rowHeadCells.add(r1);
     55         rowHeadCells.add(r2);
     56         rowHeadCells.add(r3);
     57         rowHeadCells.add(r4);
     58         rowHeadCells.add(r5);
     59 
     60         // 新建一个纵向表头
     61         HeadCell c1 = new HeadCell(UUID.randomUUID().toString(), "c1", null, 0);
     62         HeadCell c2 = new HeadCell(UUID.randomUUID().toString(), "c2", null, 0);
     63         HeadCell c3 = new HeadCell(UUID.randomUUID().toString(), "c3", null, 0);
     64         HeadCell c4 = new HeadCell(UUID.randomUUID().toString(), "c4", null, 0);
     65         HeadCell c5 = new HeadCell(UUID.randomUUID().toString(), "c5", null, 0);
     66         HeadCell c1_1 = new HeadCell(UUID.randomUUID().toString(), "c1_1", c1, 1);
     67         HeadCell c1_2 = new HeadCell(UUID.randomUUID().toString(), "c1_2", c1, 1);
     68         HeadCell c2_1 = new HeadCell(UUID.randomUUID().toString(), "c2_1", c2, 1);
     69         HeadCell c2_2 = new HeadCell(UUID.randomUUID().toString(), "c2_2", c2, 1);
     70         HeadCell c3_1 = new HeadCell(UUID.randomUUID().toString(), "c3_1", c3, 1);
     71         HeadCell c3_2 = new HeadCell(UUID.randomUUID().toString(), "c3_2", c3, 1);
     72         HeadCell c4_1 = new HeadCell(UUID.randomUUID().toString(), "c4_1", c4, 1);
     73         HeadCell c4_2 = new HeadCell(UUID.randomUUID().toString(), "c4_2", c4, 1);
     74         HeadCell c5_1 = new HeadCell(UUID.randomUUID().toString(), "c5_1", c5, 1);
     75         HeadCell c5_2 = new HeadCell(UUID.randomUUID().toString(), "c5_2", c5, 1);
     76         c1.getChildren().add(c1_1);
     77         c1.getChildren().add(c1_2);
     78         c2.getChildren().add(c2_1);
     79         c2.getChildren().add(c2_2);
     80         c3.getChildren().add(c3_1);
     81         c3.getChildren().add(c3_2);
     82         c4.getChildren().add(c4_1);
     83         c4.getChildren().add(c4_2);
     84         c5.getChildren().add(c5_1);
     85         c5.getChildren().add(c5_2);
     86 
     87         colHeadCells.add(c1);
     88         colHeadCells.add(c2);
     89         colHeadCells.add(c3);
     90         colHeadCells.add(c4);
     91         colHeadCells.add(c5);
     92     }
     93 
     94     /**
     95      * 生产一个table
     96      */
     97     @Test
     98     public void getTable() {
     99         prepare();// 准备数据
    100         StringBuilder statHtml = new StringBuilder();
    101         // 创建thead标题行
    102         statHtml.append("<table border = '1' id='expTable' name='statTable'>");
    103         statHtml.append("<thead>");
    104         List<Th> ths = new ArrayList<Th>();
    105         // 这里获得的是纵向表头的最大深度,这是因为我们在生成表格的时候
    106         // 1.如果是二维表,那table的表头并不是我们的单元格对象,我们可以通过这个集合生成表头的rowspan
    107         // 2.我们获得每个th的rowspan,从级的方面考虑,除了最低一级的th,上层的th他的rowspan只能是1,
    108         // 那么最后一级是怎么判断的呢,我们是用最后一级的深度(deep),拿最大的一级减去他的深度加1不就是他所占的rowspan吗
    109         int maxDeep = getMaxDeep(colHeadCells);
    110         getColThs(colHeadCells, ths, maxDeep);
    111         // 这个ths是用来做什么的呢
    112         // 在这里我们为th集合分了组,他的行号(rowNum)就是我们的tr,一行可以生产一个tr
    113         statHtml.append(productColTh(ths, rowHeadCells, colHeadCells));
    114         statHtml.append("</thead>");
    115         statHtml.append("<tbody>");
    116         String row = buildContext(rowHeadCells, colHeadCells);
    117         row = row.replace("</th><tr>", "</th>");
    118         statHtml.append(row);
    119         statHtml.append("</tbody>");
    120         statHtml.append("</table>");
    121         System.out.println(statHtml.toString());
    122     }
    123 
    124     /**
    125      * 这里我们就要生产tbody的部分,先说思路
    126      * 1.纵向也是有表头的,纵向表头也是有主子关系,但是他和横向表头不同,一个对象的一层叶子节点是在一个tr中,这生成tr就显得容易很多
    127      * 2.整个生成过程和纵向表头是相反的,但是生成方式不同
    128      * 
    129      * @param rowHCs
    130      * @param colHCs
    131      * @return
    132      */
    133     public String buildContext(List<HeadCell> rowHCs, List<HeadCell> colHCs) {
    134         List<HeadCell> colHeadCells = getColTh(colHCs);
    135         StringBuffer sb = new StringBuffer();
    136         int maxDeep = getMaxDeep(rowHCs);
    137         for (HeadCell childCell : rowHCs) {
    138             sb.append(bulidCol(childCell, colHeadCells, maxDeep));
    139             sb.append("</tr>");
    140         }
    141         return sb.toString();
    142     }
    143 
    144     /**
    145      * 生成横向表头
    146      * @param headCell
    147      * @param colHCs
    148      * @param maxDeep
    149      * @return
    150      */
    151     public String bulidCol(HeadCell headCell, List<HeadCell> colHCs, int maxDeep) {
    152         StringBuffer sb = new StringBuffer();
    153         sb.append("<tr>");
    154         if (headCell.getChildren() != null && headCell.getChildren().size() > 0) {
    155             Integer rowNum = headCell.getRowNum() == 0 ? 1 : headCell.getRowNum();
    156             sb.append("<th id='" + headCell.getId() + "' rowspan='" + rowNum + "' deep='" + (headCell.getDeep() + 1) + "'>" + headCell.getName() + "</th>");
    157             for (HeadCell childCell : headCell.getChildren()) {
    158                 sb.append(bulidCol(childCell, colHCs, maxDeep));
    159             }
    160         } else {
    161             int rowNum = headCell.getRowNum() == 0 ? 1 : headCell.getRowNum();
    162             int colNum = maxDeep - headCell.getDeep() + 1;
    163             int deep = headCell.getDeep() + 1;
    164             sb.append("<th name='boottom' id='" + headCell.getId() + "' rowspan='" + rowNum + "' colspan='" + colNum + "' deep='" + deep + "'>" + headCell.getName() + "</th>");
    165             for (HeadCell colCell : colHCs) {
    166                 sb.append("<td>0.0</td>");
    167             }
    168         }
    169         return sb.toString();
    170     }
    171 
    172     /**
    173      * 获得最底层的headCell的集合
    174      * @param colHCs
    175      * @return
    176      */
    177     public List<HeadCell> getColTh(List<HeadCell> colHCs) {
    178         List<HeadCell> bottomRowHead = new ArrayList<HeadCell>();
    179         for (HeadCell rowCell : colHCs) {
    180             bottomRowHead.addAll(builds(rowCell));
    181         }
    182         return bottomRowHead;
    183     }
    184 
    185     public static List<HeadCell> builds(HeadCell headCell) {
    186         List<HeadCell> bottom = new ArrayList<HeadCell>();
    187         if (headCell.getChildren() != null && headCell.getChildren().size() > 0) {
    188             for (HeadCell cheadCell : headCell.getChildren()) {
    189                 bottom.addAll(builds(cheadCell));
    190             }
    191         } else {
    192             bottom.add(headCell);
    193         }
    194         return bottom;
    195     }
    196 
    197     public String productColTh(List<Th> ths, List<HeadCell> rowHCs, List<HeadCell> colHCs) {
    198         StringBuffer sb = new StringBuffer();
    199         boolean headtitle = true;
    200         int rowspan = getMaxDeep(colHCs) + 1;
    201         int colspan = getMaxDeep(rowHCs) + 1;
    202         try {
    203             Map<Integer, List<Th>> maps = ListUtils.groupByProperty(ths, "rowNum");// 按照对象中某个字段分组
    204             for (Entry<Integer, List<Th>> entry : maps.entrySet()) {
    205                 sb.append("<tr>");
    206                 if (headtitle) {
    207                     sb.append("<th rowspan='" + rowspan + "' colspan='" + colspan + "'></th>");
    208                     headtitle = false;
    209                 }
    210                 for (Th th : entry.getValue()) {// 生产纵向表头
    211                     sb.append(th.getThStr());
    212                 }
    213                 sb.append("</tr>");
    214             }
    215         } catch (Exception e) {
    216 
    217         }
    218         return sb.toString();
    219     }
    220 
    221     public void getColThs(List<HeadCell> colHCs, List<Th> ths, int maxDeep) {
    222         for (HeadCell hc : colHCs) {
    223             if (hc.getChildren() != null && hc.getChildren().size() > 0) {
    224                 Th th = new Th();
    225                 th.setRowNum(hc.getDeep());
    226                 th.setThId(hc.getId());
    227                 th.setColspan(hc.getRowNum());
    228                 th.setRowspan(1);
    229                 if (hc.getParent() != null) {
    230                     th.setpId(hc.getParent().getId());
    231                 }
    232                 th.setValue(hc.getName());
    233                 ths.add(th);
    234                 getColThs(hc.getChildren(), ths, maxDeep);
    235             } else {
    236                 Th th = new Th();
    237                 th.setRowNum(hc.getDeep());
    238                 th.setThId(hc.getId());
    239                 th.setColspan(hc.getRowNum());
    240                 th.setRowspan(maxDeep - hc.getDeep() + 1);
    241                 if (hc.getParent() != null) {
    242                     th.setpId(hc.getParent().getId());
    243                 }
    244                 th.setValue(hc.getName());
    245                 ths.add(th);
    246             }
    247         }
    248     }
    249 
    250     /**
    251      * 求表格对象集合的最大深度
    252      * 
    253      * @param headCells
    254      *            单元格集合对象
    255      * @return 最大深度
    256      */
    257     public static Integer getMaxDeep(List<HeadCell> headCells) {
    258         if (headCells != null && headCells.size() > 0) {
    259             List<Integer> deep = new ArrayList<Integer>();
    260             for (HeadCell headCell : headCells) {
    261                 deep.add(maxDeep(headCell));
    262             }
    263             return Collections.max(deep);
    264         } else {
    265             return 1;
    266         }
    267     }
    268 
    269     private static int maxDeep(HeadCell headCell) {
    270         int i = 0;
    271         if (headCell.getChildren() != null && headCell.getChildren().size() > 0) {
    272             for (HeadCell childCell : headCell.getChildren()) {
    273                 int j = maxDeep(childCell);
    274                 i = j > i ? j : i;
    275             }
    276         } else {
    277             i = headCell.getDeep();
    278         }
    279         return i;
    280     }
    281 }

    这是生成纵向表头的类

     1 package com.sun.stat.component;
     2 
     3 import org.apache.commons.lang3.StringUtils;
     4 
     5 /**
     6  * 用于生成横向表头的html
     7  * 这里这样设计的原因是我们的表格是按照横向生成也就是一个tr一个tr的生成,然而
     8  * 横向表头只有上下级有主子关系,所以我这里用rowNum来区分它们,最终达到我生成tr的效果
     9  */
    10 public class Th {
    11     private int colspan = 1;// 合并列
    12     private int rowspan = 1;// 合并行
    13     private String thStr;// html代码
    14     private String value;// th标签文本值
    15     private int rowNum;// 行号
    16     private String thId;//这里的id和pid主要用于一个js的功能,即当一行或者一列的td全为空时隐藏掉当前行
    17     private String pId;
    18 
    19     public int getRowNum() {
    20         return rowNum;
    21     }
    22 
    23     public void setRowNum(int rowNum) {
    24         this.rowNum = rowNum;
    25     }
    26 
    27     public int getColspan() {
    28         return colspan;
    29     }
    30 
    31     public void setColspan(int colspan) {
    32         this.colspan = colspan;
    33     }
    34 
    35     public int getRowspan() {
    36         return rowspan;
    37     }
    38 
    39     public void setRowspan(int rowspan) {
    40         this.rowspan = rowspan;
    41     }
    42 
    43     public String getThStr() {
    44         StringBuilder thHtml = new StringBuilder();
    45         if (colspan == 0) {
    46             colspan = 1;
    47         }
    48         if (rowspan == 0) {
    49             rowspan = 1;
    50         }
    51         if (StringUtils.isBlank(pId)) {
    52             pId = "";
    53         }
    54         thHtml.append("<th id='").append(thId).append("'").append(" pId='").append(pId).append("' deep='").append(rowNum + 1).append("' colspan='").append(colspan).append("' rowspan='").append(rowspan);
    55         thHtml.append("'>").append(value).append("</th>");
    56         return thHtml.toString();
    57     }
    58 
    59     public void setThStr(String thStr) {
    60         this.thStr = thStr;
    61     }
    62 
    63     public String getValue() {
    64         return value;
    65     }
    66 
    67     public void setValue(String value) {
    68         this.value = value;
    69     }
    70 
    71     public String getThId() {
    72         return thId;
    73     }
    74 
    75     public void setThId(String thId) {
    76         this.thId = thId;
    77     }
    78 
    79     public String getpId() {
    80         return pId;
    81     }
    82 
    83     public void setpId(String pId) {
    84         this.pId = pId;
    85     }
    86 }

    生成的html代码就是我们需要的统计表table了,我们的表格生成也就完成了。

    当然这里涉及了合计,多项拆分,合并拆分的问题,这些都可以通过业务逻辑判断,并不影响表格的生成.

    这里涉及了我博客中的一个工具类方法,还有我为什么要生成id和pid呢,也是我后面需要的一个功能,即隐藏0值的功能,可以在我的博客中找到,这里不再赘述。

  • 相关阅读:
    hihoCoder 1092 : Have Lunch Together
    UVa 11401 三角形的个数
    2020杭电多校第一场 hdu6756 Finding a MEX
    2020杭电多校第二场 hdu6774 String Distance
    2020杭电多校第一场 hdu6759 Leading Robots
    2020牛客暑期多校训练营(第二场)A、B、C、D、F、G、H、J
    2020牛客暑期多校训练营(第二场)All with Pairs
    2020牛客暑期多校训练营(第二场)Boundary
    2020牛客暑期多校训练营(第二场)Just Shuffle
    2020牛客暑期多校训练营(第二场)Happy Triangle
  • 原文地址:https://www.cnblogs.com/sun-space/p/5592592.html
Copyright © 2011-2022 走看看