应项目的要求,改了订单导出的功能,项目上顺便又改造了导出的导出的框架,从原本的流读取,换成的现在的开源项目EasyExcel。再后来,发现我做的导出少了原本的单元格合并。。。。。。。
效果如上,就是部分公共信息是合并单元格显示的,到了商品上又不是合并,总价上又是合并的
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
对于合并单元格,官网是这样的写的:
这样写的比较笼统,不太容易看得懂,但是最后一句很关键:“合并策略可以自己写”
EasyExcel官网地址:https://alibaba-easyexcel.github.io/index.html
由于项目需要合并的复杂程度,这里我们的合并策略肯定要自己写的,但是我百度一下发现。。。。。全是把官网内容拷过来的,就没有一个人去实战的吗?
最后,好在这位老哥帮我充分理解了如何自定义策略:【转】利用阿里研发的easyexcel导入导出excel,避免OOM,并对excel加密保护
下面我们来通过我的操作来一步步讲解如何自定义合并策略
1.合并策略的编写,在如官网所描述的,你要做合并单元格,那么你就得先提供一个合并策略,共用的合并策略有以下两种:LoopMergeStrategy(循环合并),OnceAbsoluteMergeStrategy(一次性合并)
我这里拿一次性合并(OnceAbsoluteMergeStrategy)的源码来说一下:
public class OnceAbsoluteMergeStrategy extends AbstractMergeStrategy {
private int firstRowIndex;
private int lastRowIndex;
private int firstColumnIndex;
private int lastColumnIndex;
public OnceAbsoluteMergeStrategy(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) {
if (firstRowIndex >= 0 && lastRowIndex >= 0 && firstColumnIndex >= 0 && lastColumnIndex >= 0) {
this.firstRowIndex = firstRowIndex;
this.lastRowIndex = lastRowIndex;
this.firstColumnIndex = firstColumnIndex;
this.lastColumnIndex = lastColumnIndex;
} else {
throw new IllegalArgumentException("All parameters must be greater than 0");
}
}
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (cell.getRowIndex() == this.firstRowIndex && cell.getColumnIndex() == this.firstColumnIndex) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(this.firstRowIndex, this.lastRowIndex, this.firstColumnIndex, this.lastColumnIndex);
sheet.addMergedRegionUnsafe(cellRangeAddress);
}
}
}
首先,我们要重构类方法和重构merge方法,重构类方法一般是用来做数据处理和数据采集,将采集到的数据供merge方法使用。好像这里,它就是将要合并的行数以及列记录下来,再拿去merge去判断是否需要合并。
然后,merge方法是在写excel表时执行的,所以它这里入参有sheet(哪个sheet页)和cell(单元格),cell这里有重点说一下,EasyExcel写excel表时是一个一个单元格去写的,如这次进merge写的是A1单元格,下次进merge写的是A2单元格,所以merge是每一个单元格写的时候都会执行一次的。(merge会执行多次哦,别写循环了,很容易死循环的。)
说完源码,来看看我写的合并策略
public class OrderAbsoluteMergeStrategy extends AbstractMergeStrategy {
private List<Integer> colMergeList;
//<行号,往下数量>
private Map<Integer, Long> map = new HashMap<>();
public OrderAbsoluteMergeStrategy(List<?> data, Class clazz) throws Excel4JException {
List<OrderExportDto> list = (List<OrderExportDto>) data;
int currentRowNo = list.size() + 1;
if (list.size() == 0) {
return;
}
if (colMergeList == null) {
colMergeList = UtilsMy.getColMerge(clazz);
}
if (colMergeList.size() == 0) {
return;
}
Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(OrderExportDto::getOrderNo, Collectors.counting()));
String lastOrderNo = "";
for (int i = 0; i < list.size(); i++) {
String orderNo = list.get(i).getOrderNo();
if (orderNo.equals(lastOrderNo)) {
lastOrderNo = orderNo;
continue;
}
Long aLong = collect.get(orderNo);
if (aLong > 1) {
map.put(currentRowNo - list.size() + i, aLong - 1);
}
lastOrderNo = orderNo;
}
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {
if (map.containsKey(cell.getRowIndex()) && colMergeList.contains(cell.getColumnIndex())){
Long b = map.get(cell.getRowIndex());
Integer a= cell.getRowIndex();
Integer column = cell.getColumnIndex();
CellRangeAddress cellRangeAddress = new CellRangeAddress(a, a + b.intValue(), column, column);
sheet.addMergedRegionUnsafe(cellRangeAddress);
}
}
}
入参是需要导出的数据data,导出的类clazz,因为是自定义的合并列,我们无法做到直接告诉它哪写列是需要合并的,所以我们用了自定义注解的方式去获取需要合并的类,通过UtilsMy.getColMerge方法可以获取到需要合并列的一个集合。然后,再通过对数据集合的排序,筛选(需求是相同订单号的合并,商品分开展示),最终形成 <行号,往下合并多少行> map。merge方法里面判断,这一行这一列是否需要合并,如果需要则合并,往下合并a+b行。
补充部分代码:
UtilsMy.getColMerge方法:
public class UtilsMy {
public static List<Integer> getColMerge(Class<?> clz) throws Excel4JException {
List<Integer> list = new ArrayList<>();
List<Field> fields = new ArrayList();
for(Class clazz = clz; clazz != Object.class; clazz = clazz.getSuperclass()) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
}
//字段的反射顺序和编写时的顺序是一样的
int relay=0;
boolean jump=false;
for(int i=0;i<fields.size();i++){
Field field = fields.get(i);
if (field.isAnnotationPresent(ExcelField.class) || field.isAnnotationPresent(ExcelProperty.class)) {
if (field.isAnnotationPresent(ExcelFieldMy.class)) {
ExcelFieldMy ermy = (ExcelFieldMy) field.getAnnotation(ExcelFieldMy.class);
if (ermy.isMerge()) {
list.add(i - relay);
}
}
jump = true;
}
if(!jump){
relay++;
}
}
return list;
}
}
实体类(@ExcelFieldMy 为需要合并的列):
@ExcelProperty(value = "店铺名称", index = 3)@ExcelFieldMy(isMerge = true)
private String storeName;
@ExcelProperty(value = "店铺属性", index = 4)@ExcelFieldMy(isMerge = true)
private String isEshop;
@ExcelProperty(value = "业务类型", index = 5)@ExcelFieldMy(isMerge = true)
private String businessType;
@ExcelProperty(value = "订单来源", index = 6)@ExcelFieldMy(isMerge = true)
private String orderSource;
@ExcelProperty(value = "订单属性", index = 7)@ExcelFieldMy(isMerge = true)
private String goodsTypeNum;
@ExcelProperty(value = "订单类型", index = 8)@ExcelFieldMy(isMerge = true)
private String orderType;
@ExcelProperty(value = "订单编号", index = 9)@ExcelFieldMy(isMerge = true)
private String orderNo;
@ExcelProperty(value = "订单状态", index = 10)@ExcelFieldMy(isMerge = true)
private String serviceStatus;
@ExcelProperty(value = "物流方式", index = 11)@ExcelFieldMy(isMerge = true)
private String deliveryType;
@ExcelProperty(value = "配送方式", index = 12)@ExcelFieldMy(isMerge = true)
private String distributionType;
@ExcelProperty(value = "商品名称", index = 13)
private String productName;
@ExcelProperty(value = "商品SPU编码",index = 14)
private String productCode;
@ExcelProperty(value = "商品编码(sku)", index = 15)
private String skuCode;
@ExcelProperty(value = "SPU编码备注",index = 16)
private String realProductCode;
@ExcelProperty(value = "SKU编码备注",index = 17)
private String realSkuCode;
@ExcelProperty(value = "规格", index = 18)
private String skuInfo;
@NumberFormat("0.00")
@ExcelProperty(value = "单类商品原金额", index = 19)
private Double detOriPrice;
2.注册合并策略
合并策略写完了,后面就简单了,我们只需要将合并策略注册上次就好了
核心代码:
/**
* @Title: 合并的写
* @Author wuxiangen
* @param fileName
* @param sheetName
* @param excludeColumnFiledNames
* @param dataList
* @param clazz
*/
public static <T extends EasyBaseModel> void mergeWrite(String fileName, String sheetName, Set<String> excludeColumnFiledNames,
List<T> dataList, Class<T> clazz, AbstractMergeStrategy abstractMergeStrategy) {
checkSheetName(sheetName);
checkFileName(fileName);
checkDataList(dataList);
checkClazz(clazz);
if (CollectionUtils.isEmpty(excludeColumnFiledNames)) {
EasyExcel.write(fileName, clazz).sheet(sheetName).registerWriteHandler(abstractMergeStrategy).doWrite(dataList);
} else {
EasyExcel.write(fileName, clazz).excludeColumnFiledNames(excludeColumnFiledNames).sheet(sheetName).registerWriteHandler(abstractMergeStrategy).doWrite(dataList);
}
}
具体的工具类上传到csdn上,请自取。没有分的,可以评论留取邮箱地址,我看到后会第一时间发送给您。
https://download.csdn.net/download/a81579261/12037078