Java实操 -> 基于注解、反射实现Excel导出动态合并

Java资料站

共 10412字,需浏览 21分钟

 ·

2021-10-01 02:52

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

一、效果演示及相关说明
由于项目信息不能泄露,这里采用测试数据,下面的测试数据是手动输入的,仅用来辅助说明下面的解释
测试数据原始效果

合并后的效果

二、首先创建注解类。
作用:加载导出字段上,order 表示分组次数。0表示一次分组,1表示二次分组,依次类推
isflag 表示分组策略,如果为true,则用来这个字段作为当前合并的依据,所以和该字段order相同的字段都会合并

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author hhb
 * @date :2021/9/16 13:58
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MergeFlag {
    int order() default 0;

    boolean isflag() default false;
}

三、自定义合并策略
这里使用的是alibabaEasyExcel的Excel处理框架,相关导出、导入实现如果不懂请查看官方文档
https://www.yuque.com/easyexcel/doc/easyexcel

根据官方提供的单次合并策略OnceAbsoluteMergeStrategy可知,实现合并的核心代码如下

 CellRangeAddress cellRangeAddress = new CellRangeAddress(this.firstRowIndex, this.lastRowIndex, this.firstColumnIndex, this.lastColumnIndex);
        writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress);

所以我们接下来需要想办法先知道那些数据是需要合并的,所以我们要预处理导出的数据.
然后根据数据上面的注解解析出一个合并map,map的key是 order,value是一个对象,包含通过反射得到的Field[]数组的index索引、合并标识。

详细见代码

/**
     * 生成合并策略Map
     * @param dataList
     * @return
     */
    private  void generateMergeMap(List dataList,Class<?> type){
        //解析分组依据
        List<MergeFlagDTO> mergeFlagDTOList = this.analyzeData(type);
        Object preObj=null;
        for (int i=0;i<dataList.size();i++){
            //获取合并表示
            Object currObj=dataList.get(i);
            if (null!=preObj){
                //处理数据
                try {
                    this.handData(0,mergeFlagDTOList,currObj,preObj,i);
                } catch (IllegalAccessException e) {
                    throw new BadRequestAlertException("导出时发生解析错误!");
                }
            }
            preObj=currObj;
        }
    }

/**
     * 解析数据
     * @param type
     * @return
     */
    private List<MergeFlagDTO> analyzeData(Class<?> type) {
        Map<Integer, MergeFlagDTO> mergeOrderMap=new HashMap<>();
        Field[] fields = type.getDeclaredFields();
        //获取注解列表
        for (int i=0;i<fields.length;i++){
            MergeFlag mergeFlag = fields[i].getAnnotation(MergeFlag.class);
            if (null!=mergeFlag){
                //加入索引
                MergeFlagDTO dto =mergeOrderMap.get(mergeFlag.order())==null?new MergeFlagDTO():mergeOrderMap.get(mergeFlag.order());
                dto.getMergeIndexs().add(i);
                dto.setOrder(mergeFlag.order());
                if (mergeFlag.isflag()){
                    dto.setField(fields[i]);
                }
                mergeOrderMap.put(mergeFlag.order(),dto);
            }
        }
        return new ArrayList<>(mergeOrderMap.values()).stream().filter(item -> null != item.getField()).sorted(Comparator.comparing(MergeFlagDTO::getOrder)).collect(Collectors.toList());
    }

 /**
     * 处理数据
     * @param start
     * @param mergeFlagDTOList
     * @param currObj
     * @param preObj
     * @param index
     * @throws IllegalAccessException
     */
    private void handData(int start,  List<MergeFlagDTO> mergeFlagDTOList, Object currObj, Object preObj, int index) throws IllegalAccessException {
        if (start<mergeFlagDTOList.size()){
            MergeFlagDTO dto = mergeFlagDTOList.get(start);
            Field field = dto.getField();
            field.setAccessible(true);
            Object currValue = field.get(currObj);
            Object preValue = field.get(preObj);
            if (currValue.equals(preValue)) {
                this.fillMergeMap(dto.getMergeIndexs(),index);
                //继续递归
                this.handData(start+1,mergeFlagDTOList,currObj,preObj,index);
            }
        }
    }

    /**
     * 填充合并map
     * @param key
     * @param index
     */
    private void fillMergeMap(Integer key, Integer index){
        List<RowRangeDTO> rangeDTOS = mergeMap.get(key)==null?new ArrayList<>():mergeMap.get(key);
        //判断是否需要新加
        boolean needAdd=true;
        //遍历
        for (RowRangeDTO rangeDTO:rangeDTOS){
            if (rangeDTO.getEndIndex().equals(index)){
                rangeDTO.setEndIndex(index+1);
                needAdd=false;
            }
        }
        //如果没有匹配上的就说明需要
        if (needAdd){
            rangeDTOS.add(new RowRangeDTO(index,index+1));
        }
        mergeMap.put(key,rangeDTOS);
    }

最后根据合并map对数据进行合并操作

 @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer integer){
        //每一个cell只合并一次
        if (cell.getRowIndex()==1&&cell.getColumnIndex()==0){
            mergeMap.keySet().forEach(key-> mergeMap.get(key).forEach(rowRangeDTO -> sheet.addMergedRegionUnsafe(new CellRangeAddress(rowRangeDTO.getStartIndex(),rowRangeDTO.getEndIndex(),key,key))));
        }
    }


  作者 |  樱岛麻衣Ss

来源 |  cnblogs.com/ydmysm/p/merge_excel.html


加锋哥微信: java3459  
围观锋哥朋友圈,每天推送Java干货!

浏览 97
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报