upadte 优化Excel单元格合并代码逻辑,明确处理类职责

This commit is contained in:
秋辞未寒 2025-08-12 18:26:18 +08:00
parent 8f3a1b589e
commit ae5bec994d
2 changed files with 169 additions and 134 deletions

View File

@ -0,0 +1,152 @@
package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.SneakyThrows;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.CellMerge;
import java.lang.reflect.Field;
import java.util.*;
/**
* 单元格合并处理器
*
* @author Lion Li
*/
public class CellMergeHandler {
private final boolean hasTitle;
private int rowIndex;
private CellMergeHandler(final boolean hasTitle) {
this.hasTitle = hasTitle;
// 行合并开始下标
this.rowIndex = hasTitle ? 1 : 0;
}
@SneakyThrows
public List<CellRangeAddress> handle(List<?> list) {
List<CellRangeAddress> cellList = new ArrayList<>();
if (CollUtil.isEmpty(list)) {
return cellList;
}
Class<?> clazz = list.get(0).getClass();
boolean annotationPresent = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class);
Field[] fields = ReflectUtils.getFields(clazz, field -> {
if ("serialVersionUID".equals(field.getName())) {
return false;
}
if (field.isAnnotationPresent(ExcelIgnore.class)) {
return false;
}
return !annotationPresent || field.isAnnotationPresent(ExcelProperty.class);
});
// 有注解的字段
List<Field> mergeFields = new ArrayList<>();
List<Integer> mergeFieldsIndex = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(CellMerge.class)) {
CellMerge cm = field.getAnnotation(CellMerge.class);
mergeFields.add(field);
mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
if (hasTitle) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
rowIndex = Math.max(rowIndex, property.value().length);
}
}
}
Map<Field, RepeatCell> map = new HashMap<>();
// 生成两两合并单元格
for (int i = 0; i < list.size(); i++) {
Object rowObj = list.get(i);
for (int j = 0; j < mergeFields.size(); j++) {
Field field = mergeFields.get(j);
Object val = ReflectUtils.invokeGetter(rowObj, field.getName());
int colNum = mergeFieldsIndex.get(j);
if (!map.containsKey(field)) {
map.put(field, new RepeatCell(val, i));
} else {
RepeatCell repeatCell = map.get(field);
Object cellValue = repeatCell.value();
if (cellValue == null || "".equals(cellValue)) {
// 空值跳过不合并
continue;
}
if (!cellValue.equals(val)) {
if ((i - repeatCell.current() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
} else if (i == list.size() - 1) {
if (!isMerge(list, i, field)) {
// 如果最后一行不能合并检查之前的数据是否需要合并
if (i - repeatCell.current() > 1) {
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
} else if (i > repeatCell.current()) {
// 如果最后一行可以合并则直接合并到最后
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex, colNum, colNum));
}
} else if (!isMerge(list, i, field)) {
if ((i - repeatCell.current() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.current() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
}
}
}
}
return cellList;
}
private boolean isMerge(List<?> list, int i, Field field) {
boolean isMerge = true;
CellMerge cm = field.getAnnotation(CellMerge.class);
final String[] mergeBy = cm.mergeBy();
if (StrUtil.isAllNotBlank(mergeBy)) {
//比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真
for (String fieldName : mergeBy) {
final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName);
final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName);
if (!Objects.equals(valPre, valCurrent)) {
//依赖字段如有任一不等值,则标记为不可合并
isMerge = false;
}
}
}
return isMerge;
}
record RepeatCell(Object value, int current) {}
/**
* 创建一个单元格合并处理器实例
*
* @param hasTitle 是否合并标题
* @return 单元格合并处理器
*/
public static CellMergeHandler of(final boolean hasTitle) {
return new CellMergeHandler(hasTitle);
}
/**
* 创建一个单元格合并处理器实例默认不合并标题
*
* @return 单元格合并处理器
*/
public static CellMergeHandler of() {
return new CellMergeHandler(false);
}
}

View File

@ -1,26 +1,15 @@
package org.dromara.common.excel.core; package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.metadata.Head; import cn.idev.excel.metadata.Head;
import cn.idev.excel.write.handler.WorkbookWriteHandler; import cn.idev.excel.write.handler.WorkbookWriteHandler;
import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext; import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext;
import cn.idev.excel.write.merge.AbstractMergeStrategy; import cn.idev.excel.write.merge.AbstractMergeStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.CellMerge;
import java.lang.reflect.Field;
import java.util.*; import java.util.*;
/** /**
@ -32,21 +21,22 @@ import java.util.*;
public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler { public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler {
private final List<CellRangeAddress> cellList; private final List<CellRangeAddress> cellList;
private final boolean hasTitle;
private int rowIndex; public CellMergeStrategy(List<CellRangeAddress> cellList) {
this.cellList = cellList;
}
public CellMergeStrategy(List<?> list, boolean hasTitle) { public CellMergeStrategy(List<?> list, boolean hasTitle) {
this.hasTitle = hasTitle; this.cellList = CellMergeHandler.of(hasTitle).handle(list);
// 行合并开始下标
this.rowIndex = hasTitle ? 1 : 0;
this.cellList = handle(list, hasTitle);
} }
@Override @Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (CollUtil.isEmpty(cellList)){
return;
}
//单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空 //单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空
final int rowIndex = cell.getRowIndex(); final int rowIndex = cell.getRowIndex();
if (CollUtil.isNotEmpty(cellList)){
for (CellRangeAddress cellAddresses : cellList) { for (CellRangeAddress cellAddresses : cellList) {
final int firstRow = cellAddresses.getFirstRow(); final int firstRow = cellAddresses.getFirstRow();
if (cellAddresses.isInRange(cell) && rowIndex != firstRow){ if (cellAddresses.isInRange(cell) && rowIndex != firstRow){
@ -54,123 +44,16 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook
} }
} }
} }
}
@Override @Override
public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) { public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) {
if (CollUtil.isEmpty(cellList)){
return;
}
//当前表格写完后统一写入 //当前表格写完后统一写入
if (CollUtil.isNotEmpty(cellList)){
for (CellRangeAddress item : cellList) { for (CellRangeAddress item : cellList) {
context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item); context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item);
} }
} }
}
@SneakyThrows
private List<CellRangeAddress> handle(List<?> list, boolean hasTitle) {
List<CellRangeAddress> cellList = new ArrayList<>();
if (CollUtil.isEmpty(list)) {
return cellList;
}
Class<?> clazz = list.get(0).getClass();
boolean annotationPresent = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class);
Field[] fields = ReflectUtils.getFields(clazz, field -> {
if ("serialVersionUID".equals(field.getName())) {
return false;
}
if (field.isAnnotationPresent(ExcelIgnore.class)) {
return false;
}
return !annotationPresent || field.isAnnotationPresent(ExcelProperty.class);
});
// 有注解的字段
List<Field> mergeFields = new ArrayList<>();
List<Integer> mergeFieldsIndex = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(CellMerge.class)) {
CellMerge cm = field.getAnnotation(CellMerge.class);
mergeFields.add(field);
mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
if (hasTitle) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
rowIndex = Math.max(rowIndex, property.value().length);
}
}
}
Map<Field, RepeatCell> map = new HashMap<>();
// 生成两两合并单元格
for (int i = 0; i < list.size(); i++) {
Object rowObj = list.get(i);
for (int j = 0; j < mergeFields.size(); j++) {
Field field = mergeFields.get(j);
Object val = ReflectUtils.invokeGetter(rowObj, field.getName());
int colNum = mergeFieldsIndex.get(j);
if (!map.containsKey(field)) {
map.put(field, new RepeatCell(val, i));
} else {
RepeatCell repeatCell = map.get(field);
Object cellValue = repeatCell.getValue();
if (cellValue == null || "".equals(cellValue)) {
// 空值跳过不合并
continue;
}
if (!cellValue.equals(val)) {
if ((i - repeatCell.getCurrent() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
} else if (i == list.size() - 1) {
if (!isMerge(list, i, field)) {
// 如果最后一行不能合并检查之前的数据是否需要合并
if (i - repeatCell.getCurrent() > 1) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
} else if (i > repeatCell.getCurrent()) {
// 如果最后一行可以合并则直接合并到最后
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
}
} else if (!isMerge(list, i, field)) {
if ((i - repeatCell.getCurrent() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
}
}
}
}
return cellList;
}
private boolean isMerge(List<?> list, int i, Field field) {
boolean isMerge = true;
CellMerge cm = field.getAnnotation(CellMerge.class);
final String[] mergeBy = cm.mergeBy();
if (StrUtil.isAllNotBlank(mergeBy)) {
//比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真
for (String fieldName : mergeBy) {
final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName);
final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName);
if (!Objects.equals(valPre, valCurrent)) {
//依赖字段如有任一不等值,则标记为不可合并
isMerge = false;
}
}
}
return isMerge;
}
@Data
@AllArgsConstructor
static class RepeatCell {
private Object value;
private int current;
}
} }