一、导出结果

二、依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.2.1</version>
</dependency>

三、ExportData

package com.example.fileupload.service.impl;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
import lombok.Data;

@ColumnWidth(100 / 8)
@Data
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER)
public class ExportData {

    @ExcelProperty({"信息","家具编号"})
    private String assetCode;
    @ExcelProperty({"信息","家具名称"})
    private String assetName;
    @ExcelProperty({"信息","部门编号"})
    private String deptCode;
    @ExcelProperty({"信息","部门名称"})
    private String deptName;
    @ExcelProperty({"信息","用途"})
    private String usage;
    @ExcelProperty({"信息","描述"})
    private String desc;

    public ExportData(String assetCode, String assetName, String deptCode, String deptName, String usage, String desc) {
        this.assetCode = assetCode;
        this.assetName = assetName;
        this.deptCode = deptCode;
        this.deptName = deptName;
        this.usage = usage;
        this.desc = desc;
    }
}

四、合并策略CommonMergeStrategy

package com.example.fileupload.service.impl;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * 公共的合并单元格类,支持自定义合并策略
 */
public class CommonMergeStrategy extends AbstractMergeStrategy {
    // 合并策略list
    private List<CellRangeAddress> cellRangeAddresssList;

    // 通过有参数构造方法,设置合并策略
    public CommonMergeStrategy(List<CellRangeAddress> list) {
        this.cellRangeAddresssList = list;
    }

    /**
     * 重写合并策略方法
     * @param sheet sheet
     * @param cell cell
     * @param head head
     * @param relativeRowIndex relativeRowIndex
     */
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        // 将自定义合并策略假如excel
        if (CollectionUtils.isNotEmpty(cellRangeAddresssList)) {
            for (CellRangeAddress item : cellRangeAddresssList) {
                sheet.addMergedRegionUnsafe(item);
            }
        }
    }
}

五、测试类

package com.example.fileupload.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.ss.util.CellRangeAddress;
import org.junit.Test;

import java.util.List;

public class MainTest {


    @Test
    public void exportTest() {
        Assert anAssert1 = new Assert("资产编号", "资产名称",
                List.of(new Dept("部门编号1", "部门名称1",
                List.of(new Usage("用途1", "说明1"), new Usage("用途2", "说明2")))));
        Assert anAssert2 = new Assert("资产编号1", "资产名称1",
                List.of(new Dept("部门编号1", "部门名称1",
                List.of(new Usage("用途1", "说明1"))), new Dept("部门编号2", "部门名称2",
                List.of(new Usage("用途1", "说明1")))));
        Assert anAssert3 = new Assert("资产编号1", "资产名称1",
                List.of(new Dept("部门编号1", "部门名称1",
                List.of(new Usage("用途1", "说明1"), new Usage("用途2", "说明2"), new Usage("用途3", "说明3")))));
        Assert anAssert4 = new Assert("资产编号3", "资产名称3",
                List.of(new Dept("部门编号2", "部门名称2",
                List.of(new Usage("用途2", "说明2")))));
        List<Assert> anAssertList = List.of(anAssert1, anAssert2, anAssert3, anAssert4);

        ExportData exportData1 = new ExportData("资产编号", "资产名称", "部门编号1", "部门名称1", "用途1", "描述");
        ExportData exportData2 = new ExportData("资产编号", "资产名称", "部门编号1", "部门名称1", "用途2", "描述");
        ExportData exportData3 = new ExportData("资产编号1", "资产名称1", "部门编号1", "部门名称1", "用途", "描述");
        ExportData exportData4 = new ExportData("资产编号1", "资产名称1", "部门编号2", "部门名称2", "用途", "描述");
        ExportData exportData5 = new ExportData("资产编号2", "资产名称2", "部门编号2", "部门名称2", "用途1", "描述1");
        ExportData exportData6 = new ExportData("资产编号2", "资产名称2", "部门编号2", "部门名称2", "用途2", "描述2");
        ExportData exportData7 = new ExportData("资产编号2", "资产名称2", "部门编号2", "部门名称2", "用途3", "描述3");
        ExportData exportData8 = new ExportData("资产编号3", "资产名称3", "部门编号2", "部门名称2", "用途3", "描述3");

        List<ExportData> exportDataList = List.of(exportData1, exportData2, exportData3, exportData4, exportData5, exportData6, exportData7, exportData8);

        String fileName = "dynamic_merge_example.xlsx";
        ExcelWriterBuilder write = EasyExcel.write(fileName, ExportData.class);
        write.head(ExportData.class).registerWriteHandler(new CommonMergeStrategy(getCellRangeAddress(anAssertList, 2,2, 4))).sheet("hello").doWrite(exportDataList);
    }


    public List<CellRangeAddress> getCellRangeAddress(List<Assert> anAssertList, int headRow, int colStart, int colEnd) {
        List<CellRangeAddress> rangeAddresses = Lists.newArrayList();
        int start = headRow;
        int deptStart = start;
        int end = start;
        for (Assert anAssert : anAssertList) {
            for (Dept dept : anAssert.getDeptList()) {
                for (Usage usage : dept.getUsageList()) {
                    end++;
                }
                if (deptStart < end - 1) {
                    for (int i = colStart; i < colEnd; i++) {
                        rangeAddresses.add(new CellRangeAddress(deptStart, end - 1, i, i));
                    }
                }
                deptStart = end;
            }
            if (start < end - 1) {
                for (int i = 0; i < colStart; i++) {
                    rangeAddresses.add(new CellRangeAddress(start, end - 1, i, i));
                }
            }
            start = end;
        }
        return rangeAddresses;
    }
}