思路
1 将一次查询数据改成分页查询,比如一次2000条,
2 将每次查询的数据按业务分组计算每类业务 动态列追加的最大次数
// treeMap 追加列 2 在excel列表头则是追加2列,名称自定义,我这边是补数字,示例 追加列1,追加列2
// 我的业务是按数据库存放的图片来确定最大追加列,需要将图片字段按 |||拆分 。然后将内容分别填充到追加列1,追加列2 等
/**
* 计算 图片值分割后的最大次数
* @param dto
* @param treeMap key 列头中文名, value 追加的最大次数
* @param key 列头的key
* @param filedName 对象 字段
*/
private void getMaxCountImageField(Object dto, TreeMap<String, Integer> treeMap, String key, String filedName) {
try {
Object pictures = ReflectUtil.getFieldValue(dto, filedName);
if (ObjUtil.isNotNull(pictures)) {
String[] split = pictures.toString().split("\\|\\|\\|");
//取最大值
treeMap.put(key, Math.max(treeMap.getOrDefault(key, 0), split.length));
}
} catch (Exception e) {
// 添加适当的日志记录
log.error("计算动态列最大出现次数异常:", e);
}
}
3将分页数据暂时暂存redis ,key要唯一哦, 最好是 业务key+毫秒数+分页页码 示例: type1: 毫秒数:1 type2:毫秒数:1
4 循环完毕,计算每个sheet的列表头如何确定
扫描dto的注解 如@Excel @ExcelProperty 或者自定义注解,需要注意的是,该注解一定是固定列,最好有序
按上计算动态列追加次数,继续计算 sheet的列表头。代码如下:
/**
* 获取导出sheet里的所有列头 固定+动态
*
* @param c 模板类
* @param initTreeMap 动态列头集合 有序
* @param workPlanType 工单类型
* @return //返回 编号,姓名,。。 处理前1,处理前2.。。处理后1.。。。。等动态列头
*/
private List<List<String>> getFaultCellHeads(Class c, Map<Integer,TreeMap<String,Integer>> initTreeMap,Integer workPlanType) {
//固定列
List<List<String>> excelAnnotations = ExcelFileNameUtils.getExcelAnnotations(c);
//固定列 +动态列 适用其他工单
for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
String name = entry.getKey();
Integer count = entry.getValue();
//初始化1 ,map value次数等于0 的时候,不追加
int init = 1;
//可以改成stream写法
while (init <= count) {
String prefix = name + init;
List<String> list=new ArrayList<>();
list.add(prefix);
excelAnnotations.add(list);
init++;
}
}
//返回 编号,姓名,。。 处理前1,处理前2.。。处理后1.。。。。等动态列头
return excelAnnotations;
}
开始写入数据到excel ,分分页循环,挨个类型去获取缓存数据写入
需要初始化多个sheet
获取头
动态填充数据
填充方法
/**
* excel 行数据写入, 即 表头 固定列+动态列
*
* 本方法支持
* @param dataVoList 数据
* @param treeMap 动态列
* @param <T>
* @return
*/
public <T> List<List<Object>> getCommonDataExcelList(List<T> dataVoList, TreeMap<String, Integer> treeMap) {
List<List<Object>> dataLists = new ArrayList<>();
for (T dataVo : dataVoList) {
// 第一行导出数据,按序赋值
List<Object> objects = new ArrayList<>();
// 赋值
Field[] declaredFields = dataVo.getClass().getDeclaredFields();
Field beforePictureField = null;
Field afterPictureField = null;
Field picturesField = null;
// 处理固定列数据,代码属性不要错位写,有序声明
for (Field field : declaredFields) {
Excel excel = field.getAnnotation(Excel.class);
if (excel != null) {
// 铺开列,获取属性的值,属性格式化还未处理 TODO
field.setAccessible(true);
Object value = null;
try {
value = field.get(dataVo);
} catch (Exception e) {
// 出取值异常,写入空
}
objects.add(value);
}
if ("beforePicture".equals(field.getName())) {
beforePictureField = field;
field.setAccessible(true);
}
if ("afterPicture".equals(field.getName())) {
afterPictureField = field;
field.setAccessible(true);
}
if ("pictures".equals(field.getName())) {
picturesField = field;
field.setAccessible(true);
}
}
try {
//处理前动态列
if (ObjUtil.isNotNull(beforePictureField)) {
Object beforePicture = beforePictureField.get(dataVo);
if (ObjUtil.isNotNull(beforePicture)) {
Integer integer = treeMap.get(ExcelDynamicHeadConstants.BEFORE_PROCESSING);
spreadImageColumns(beforePicture.toString(), integer, objects);
}
}
//处理后动态列
if (ObjUtil.isNotNull(afterPictureField)) {
Object afterPicture = afterPictureField.get(dataVo);
if (ObjUtil.isNotNull(afterPicture)) {
Integer integer = treeMap.get(ExcelDynamicHeadConstants.AFTER_PROCESSING);
spreadImageColumns(afterPicture.toString(), integer, objects);
}
}
//处理图片动态列
if (ObjUtil.isNotNull(picturesField)) {
Object pictures = picturesField.get(dataVo);
if (ObjUtil.isNotNull(pictures)) {
Integer integer = treeMap.get(ExcelDynamicHeadConstants.IMAGE_PROCESSING);
spreadImageColumns(pictures.toString(), integer, objects);
}
}
} catch (Exception e) {
log.error("写入excel行数据,包含动态头报错:", e);
}
dataLists.add(objects);
}
return dataLists;
}
注意需要处理无任务数据需要写个空白页
最后
excelWriter.finish();
以上是核心逻辑代码,有不懂可以留言
这是我的excel导出样式,有多个sheet,每个sheet固定列和动态追加列,都根据查询条件变动