JeecgFramework的AutoPoi导出Excel,合并单元格并修改单元格格式为数字,数字求和值错误过大怎么办

之前做了一个功能,在前端的呈现有根据订单合并单元格,但是导出Excel并没有做合并的功能,而且客户提出,导出的数字无法进行求和操作。

这两个问题都很简单,首先合并单元格,只需要在@Excel注解后面加上合并相关参数即可。
合并单元格:

@Excel(name = "数量", width = 15, mergeVertical = true, mergeRely = {1, 11})
private BigDecimal quantity;

其中,mergeVertical指的是垂直合并,设置为true即开启。mergeRely为合并依据,比如上述代码中设置的,为第2列和第12列相同时合并,第12列是本列,第一列可能为订单号,这里可以设置多列。如果不设置,则只要本列相同就合并。
设置数字格式:

@Excel(name = "数量", width = 15, type = 4)
private BigDecimal quantity;

这里type值1为字符串,4为数字。
其实poi本来0是数字,1是字符串(可以参考poi包下的CellType类),但AutoPoi做了个转化。

到这里似乎两个问题都解决了,其实会报错:

IllegalStateException: Cannot get a STRING value from a NUMERIC cell。

于是debug追源代码,发现合并单元格的时候直接是cell.getStringCellValue(),这时候单元格格式是数字就会报错了。
源代码如下(位于org.jeecgframework.poi.util.PoiMergeCellUtil大约93行):

if (row == null || row.getCell(index) == null) {
  if (mergeDataMap.get(index) == null) {
    continue;
  }
  if (mergeDataMap.get(index).getEndRow() == 0) {
    mergeDataMap.get(index).setEndRow(i - 1);
  }
} else {
  text = row.getCell(index).getStringCellValue();
  if (StringUtils.isNotEmpty(text)) {
    hanlderMergeCells(index, i, text, mergeDataMap, sheet, row.getCell(index), mergeMap.get(index));
  } else {
    mergeCellOrContinue(index, mergeDataMap, sheet);
  }
}

可以看到else中直接获取StringCellValue的方法。
那怎么办呢?
由于工期比较急,我只能用最简单粗暴的方法了,直接把调用链上的类都复制出来,大概有下面这几个:

org.jeecgframework.poi.excel.view.JeecgEntityExcelView
org.jeecgframework.poi.excel.export.base.ExcelExportBase
org.jeecgframework.poi.excel.export.ExcelExportServer
org.jeecgframework.poi.excel.ExcelExportUtil
org.jeecgframework.poi.util.PoiMergeCellUtil

复制到自己创建的目录下,然后开始修改。为了解决上面的问题,修改的主要代码为:

…
} else {
  Cell cell = row.getCell(index);
  try {
    // 加try…catch,如果获取字符串类型值报错,则去获取数字类型值,然后转成字符串
    text = cell.getStringCellValue();
  } catch (Exception e) {
    double numericCellValue = cell.getNumericCellValue();
    text = String.valueOf(numericCellValue);
  }
  if (StringUtils.isNotEmpty(text)) {
    hanlderMergeCells(index, i, text, mergeDataMap, sheet, cell, mergeMap.get(index));
  } else {
    mergeCellOrContinue(index, mergeDataMap, sheet);
  }
}

改完之后,大功告成……
了吗?并没有,现在导出的Excel看起来单元格也合并了,单元格类型也成数字了,可是一求和发现,怎么这么大?
比如一共5行,前三行是25,后两行是20,合并单元格之后就是一个25一个20,求和应该是45,但实际结果却是115!
接下来就要解决这个问题。
还是捋代码,我们进入上面else中的hanlderMergeCells()方法,点进去,看一下逻辑:

private static void hanlderMergeCells(Integer index, int rowNum, String text, Map<Integer, MergeEntity> mergeDataMap, Sheet sheet, Cell cell, int[] delys) {
    if (mergeDataMap.containsKey(index)) {
        if (checkIsEqualByCellContents(mergeDataMap.get(index), text, cell, delys, rowNum)) {
            mergeDataMap.get(index).setEndRow(rowNum);
        } else {
            //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B
            try{
            sheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index));
            }catch (IllegalArgumentException e){
                LOGGER.error("合并单元格错误日志:"+e.getMessage());
                e.fillInStackTrace();
            }
            //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B
            mergeDataMap.put(index, createMergeEntity(text, rowNum, cell, delys));
        }
    } else {
        mergeDataMap.put(index, createMergeEntity(text, rowNum, cell, delys));
    }
}

这里可以看到,如果是合并格的第一行进来,会直接进else,否则会进入if,如果没有子表,就进入再里面一层的if,我们就在这里动手脚。
修改后的代码如下:

if (mergeDataMap.containsKey(index)) {
    if (checkIsEqualByCellContents(mergeDataMap.get(index), text, cell, delys, rowNum)) {
        try {
                        // 获取字符串类型值,如果不抛异常,则设置单元格值为空字符串
            cell.getStringCellValue();
            cell.setCellValue("");
        } catch (Exception e) {
                        // 如果抛异常,则将单元格值设置为数字0
            cell.setCellValue(0);
        }
        cell.setCellType(CellType.BLANK);
        mergeDataMap.get(index).setEndRow(rowNum);
    } else {
        ……
        }
else {……}

核心思想就是将合并单元格除首行外的其他行值都设置为空,这样就是“真·合并”了。

这个改动的位置找了好久,一开始改的是if (mergeDataMap.size() > 0) {后面的代码,发现无效,才又把代码往前提,最终成功。

这次真的大功告成了。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容