天天看點

基于注解、反射實作導出動态合并

一、效果示範及相關說明

由于項目資訊不能洩露,這裡采用測試資料,下面的測試資料是手動輸入的,僅用來輔助說明下面的解釋

測試資料原始效果

基于注解、反射實作導出動态合并

合并後的效果

基于注解、反射實作導出動态合并

二、首先建立注解類。

作用:加載導出字段上,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))));
        }
    }