记一次动态列导出

82 阅读3分钟

恭喜小花生夺得LCK冠军! 小花生

前言

在最近的一次迭代中 ,因为业务频繁的提增加标签的需求,再也受不了了于是跟产品说把标签做成了一个可配置的系统,配置好即可对物资需求的物料进行打标,大概的数据结构是如下图这样的:

image.png

底层就是一棵多叉树,每个标签分类下可以配置本树的层级,可以无限配置标签分类和每个标签的深度,因为每个物料都需要展示出各自绑定的标签,这样会导致一个情况,列表展示的列不是固定的,加上这个配置可以无限延伸,在前端展示比较好处理,但是在excel上比较繁琐,所以记录一下动态列的扩展方法: 这是之前的代码,就是很普通的easyExcel写:

EasyExcel.write(filePathName, BoardGlobalResponse.class)
.sheet()
.doWrite(retBoardGlobalResponseList);

在对标签列进行改造之后,数据库将所有的标签进行了聚合采用了json结构,效果如下:

{
    "tags": [
        {
            "tagId": 23,
            "value": "正常盯盘池子-不盯盘-/",
            "tcCode": "物料分类",
            "tcName": "物料分类",
            "tagName": "/",
            "tagIdPath": "8-12-23"
        },
        {
            "tagId": 138,
            "value": "测试多分类1",
            "tcCode": "物料分级",
            "tcName": "物料分级",
            "tagName": "/",
            "tagIdPath": "138"
        }
    ]
}

因为有多个分类都冗余在了一个字段中,所以在导出的时候,需要将该结构打平成Map方式,每个对象的map里key是标题,value是标签值,在充血模型里加入转换方法:

    @ExcelIgnore
    // 动态列
    private Map<String, String> dynamicColumns = new HashMap<>();

    public void setTagInfo(TagInfoDto tagInfo) {
        this.tagInfo = tagInfo;
        if (tagInfo == null) {
            return;
    }
        Map<String, String> maps = new HashMap<>();
        tagInfo.getTags().forEach(tag -> {
            String tagPath = tag.getValue();
            if (StringUtils.isBlank(tagPath)) {
                return;
        }
            String[] split = tagPath.split("-");
            for (int i = 1; i <= split.length; i++) {
                maps.put(String.format(tag.getTcCode() + "(%s级标签)", i), split[i - 1
            ]);
        }
            this.dynamicColumns = maps;
    });
}

由于我的物料很多,每个物料都会有自己所绑定的标签分类,数量各不相同,为了统一列,我把数据库中所有的配置分类都拿了出来:

    private List<String> getDynamicHead() {
    // 获取header
        List<TagCategoryDO> categoryDOList = tagCategoryMapper.allTagCategory();
        List<String> list = new ArrayList<>();
        categoryDOList.forEach(category ->{
            for (int i = 1; i <= category.getLevelNumb(); i++) {
                list.add(String.format(category.getTcCode() + "(%s级标签)", i));
        }
    });
        return list;
}

接下来实现自定义的行处理器,对字段往后进行拼接:

public class DynamicColumnsWriteHandler implements RowWriteHandler {

    private final List<String> dynamicHeaders;

    private Integer headerSize = 0;

    private final List<BoardGlobalResponse> dataList;


    public DynamicColumnsWriteHandler(List<String> dynamicHeaders, List<BoardGlobalResponse> dataList) {
        this.dynamicHeaders = dynamicHeaders;
        this.dataList = dataList;
    }

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex,
                                Integer relativeRowIndex, Boolean isHead) {}

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                               Integer relativeRowIndex, Boolean isHead) {}

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                                Integer relativeRowIndex, Boolean isHead) {
        // 设置头
        if (Boolean.TRUE.equals(isHead)) {
            // 获取偏移列数
            int lastRowNum = row.getLastCellNum() < 0 ? 0 : row.getLastCellNum();
            headerSize = lastRowNum + dynamicHeaders.size();
            // 获取头部列的样式模版
            Cell cellTemplate = row.getCell(relativeRowIndex);
            // 列后追加
            for (int i = 0; i < dynamicHeaders.size(); i++) {
                Cell cell = row.createCell(lastRowNum + i );
                cell.setCellValue(dynamicHeaders.get(i));
                cell.setCellStyle(cellTemplate.getCellStyle());
            }
        }
        if (Boolean.FALSE.equals(isHead)) {
            if (relativeRowIndex < dataList.size()) {
                BoardGlobalResponse dto = dataList.get(relativeRowIndex);
                // 在表头后面追加
                Map<String, String> dynamicColumns = dto.getDynamicColumns();
                int lastCellIndex = row.getLastCellNum() <= 0 ? 0 : row.getLastCellNum();
                for (int i = 0; i < dynamicHeaders.size(); i++) {
                    Cell cell = row.createCell(lastCellIndex + i);
                    String value = dynamicColumns.get(dynamicHeaders.get(i));
                    cell.setCellValue(value == null ? "": value);
                }
            }
        }
    }
}

这里主要是对表头和每行的value值进行特殊的拼接,因为这一步已经是在带有@ExcelProperty注解处理之后的处理,所以往后拼接就可以,但是要注意拼接的位置,easyExcel的拼接是从0作为索引开始的,在外层改造之后的结构如下:

   List<String> dynamicHead = getDynamicHead();
   String filePathName = "globalDashBoard-" + System.currentTimeMillis() + ".xlsx";
   EasyExcel.write(filePathName, BoardGlobalResponse.class).sheet()
            .registerWriteHandler(new DynamicColumnsWriteHandler(dynamicHead, retBoardGlobalResponseList))
            .doWrite(retBoardGlobalResponseList);

最后测试结果如下:

image.png 现在写出来也没啥难的,主要是刚开始的动态列让人脑袋头大,所以记录一下,没啥东西,如果对你有用还请给我个小小的赞 ,或者你有更好的方法也可以留言评论~