1.业务场景分析
实际业务中,我们常遇到一对多甚至多对多 的数据关系。例如,一个主实体包含多个一级子项,每个一级子项又包含多个二级子项。传统平面表格难以直观展示这种层次关系,需要合并单元格 和多级表头来优化可读性。
2.实现结果

3.依赖
<dependencies>
<!-- Apache POI 核心库 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- 用于处理 .xlsx 格式的Excel文件(Office Open XML) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
4.代码
package com.example.study.controller.easypoi;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
/**
* 嵌套列表导出控制器 - 增强空集合处理版本
* 使用Spring Boot的@RestController注解标识这是一个RESTful控制器[6,7](@ref)
* 使用@RequestMapping注解定义控制器的基础路径[6](@ref)
*/
@RestController
@RequestMapping("/api/export/two")
public class NestedListExportTwoController {
/**
* 主实体数据类 - 代表最外层的数据结构
* 使用Java Bean规范,包含私有字段和公共getter/setter方法
*/
public static class MainEntity {
private String mainId; // 主实体ID
private String mainName; // 主实体名称
private LocalDate createTime; // 创建时间,使用Java 8的LocalDate类型
private List<FirstLevelItem> firstLevelItems; // 一级子项列表,可能为null或空
// 默认构造器,Spring框架反射创建对象时需要[6](@ref)
public MainEntity() {}
// 全参数构造器,便于快速创建测试数据
public MainEntity(String mainId, String mainName, LocalDate createTime, List<FirstLevelItem> firstLevelItems) {
this.mainId = mainId;
this.mainName = mainName;
this.createTime = createTime;
this.firstLevelItems = firstLevelItems;
}
// Getters and Setters - 遵循Java Bean规范,便于框架访问属性[6](@ref)
public String getMainId() { return mainId; }
public void setMainId(String mainId) { this.mainId = mainId; }
public String getMainName() { return mainName; }
public void setMainName(String mainName) { this.mainName = mainName; }
public LocalDate getCreateTime() { return createTime; }
public void setCreateTime(LocalDate createTime) { this.createTime = createTime; }
public List<FirstLevelItem> getFirstLevelItems() { return firstLevelItems; }
public void setFirstLevelItems(List<FirstLevelItem> firstLevelItems) { this.firstLevelItems = firstLevelItems; }
}
/**
* 一级子项数据类 - 代表嵌套的第一层数据结构
*/
public static class FirstLevelItem {
private String firstId; // 一级子项ID
private String firstName; // 一级子项名称
private List<SecondLevelItem> secondLevelItems; // 二级子项列表
private Integer firstLevelCount; // 一级子项数量,可自动计算
public FirstLevelItem() {}
public FirstLevelItem(String firstId, String firstName, List<SecondLevelItem> secondLevelItems, Integer firstLevelCount) {
this.firstId = firstId;
this.firstName = firstName;
this.secondLevelItems = secondLevelItems;
this.firstLevelCount = firstLevelCount;
}
// Getters and Setters
public String getFirstId() { return firstId; }
public void setFirstId(String firstId) { this.firstId = firstId; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public List<SecondLevelItem> getSecondLevelItems() { return secondLevelItems; }
public void setSecondLevelItems(List<SecondLevelItem> secondLevelItems) { this.secondLevelItems = secondLevelItems; }
/**
* 智能获取一级子项数量:优先使用显式设置的值,否则计算二级子项列表大小
* 使用Optional类安全处理null值,避免空指针异常[1](@ref)
*/
public Integer getFirstLevelCount() {
return firstLevelCount != null ? firstLevelCount :
(secondLevelItems != null ? secondLevelItems.size() : 0);
}
public void setFirstLevelCount(Integer firstLevelCount) { this.firstLevelCount = firstLevelCount; }
}
/**
* 二级子项数据类 - 代表嵌套的第二层数据结构
*/
public static class SecondLevelItem {
private String secondId; // 二级子项ID
private String secondName; // 二级子项名称
private Integer secondCount; // 二级子项数量
private Integer secondYear; // 二级子项年份
public SecondLevelItem() {}
public SecondLevelItem(String secondId, String secondName, Integer secondCount, Integer secondYear) {
this.secondId = secondId;
this.secondName = secondName;
this.secondCount = secondCount;
this.secondYear = secondYear;
}
// Getters and Setters
public String getSecondId() { return secondId; }
public void setSecondId(String secondId) { this.secondId = secondId; }
public String getSecondName() { return secondName; }
public void setSecondName(String secondName) { this.secondName = secondName; }
public Integer getSecondCount() { return secondCount; }
public void setSecondCount(Integer secondCount) { this.secondCount = secondCount; }
public Integer getSecondYear() { return secondYear; }
public void setSecondYear(Integer secondYear) { this.secondYear = secondYear; }
}
/**
* 生成包含各种空集合情况的测试数据
* 模拟真实业务场景中可能遇到的各种数据边界情况
* 使用@return注解明确返回值类型和含义[1](@ref)
*/
private List<MainEntity> generateTestData() {
List<MainEntity> mainEntities = new ArrayList<>();
// 主实体1:正常数据(有完整的一级和二级子项)- 典型业务场景
List<FirstLevelItem> firstLevelItems1 = new ArrayList<>();
List<SecondLevelItem> secondItems1 = Arrays.asList(
new SecondLevelItem("S12-001", "二级子项1", 10, null),
new SecondLevelItem("S22-001", "二级子项2", 14, 2025)
);
firstLevelItems1.add(new FirstLevelItem("S1-001", "一级子项1", secondItems1, 2));
List<SecondLevelItem> secondItems2 = Arrays.asList(
new SecondLevelItem("S12-002", "二级子项1", 14, 2025)
);
firstLevelItems1.add(new FirstLevelItem("S2-002", null, secondItems2, 1));
MainEntity mainEntity1 = new MainEntity("M001", "主实体1", LocalDate.of(2024, 1, 15), firstLevelItems1);
// 主实体2:正常数据 - 另一个典型业务场景
List<FirstLevelItem> firstLevelItems2 = new ArrayList<>();
List<SecondLevelItem> secondItems3 = Arrays.asList(
new SecondLevelItem("S12-002", "二级子项2", 5, 2023),
new SecondLevelItem("S22-002", "二级子项2", 8, 2024),
new SecondLevelItem("S32-002", "二级子项3", 12, 2025)
);
firstLevelItems2.add(new FirstLevelItem("S1-003", "一级子项3", secondItems3, 3));
MainEntity mainEntity2 = new MainEntity("M002", "主实体2", LocalDate.of(2024, 3, 20), firstLevelItems2);
// 主实体3:一级子项列表为null(测试空指针处理)- 边界情况测试
MainEntity mainEntity3 = new MainEntity("M003", "主实体3", null, null);
// 主实体4:一级子项列表不为空但二级子项列表为null - 边界情况测试
List<FirstLevelItem> firstLevelItems4 = new ArrayList<>();
FirstLevelItem firstItem4 = new FirstLevelItem("S1-004", "一级子项4", null, 0); // 二级子项为null
firstLevelItems4.add(firstItem4);
MainEntity mainEntity4 = new MainEntity("M004", "主实体4", null, firstLevelItems4);
// 主实体5:一级子项列表为空列表(非null)- 边界情况测试
MainEntity mainEntity5 = new MainEntity("M005", "主实体5", LocalDate.of(2024, 5, 1), Collections.emptyList());
// 主实体6:一级子项有数据但二级子项为空列表 - 边界情况测试
List<FirstLevelItem> firstLevelItems6 = new ArrayList<>();
FirstLevelItem firstItem6 = new FirstLevelItem("S1-006", "一级子项6", Collections.emptyList(), 0);
firstLevelItems6.add(firstItem6);
MainEntity mainEntity6 = new MainEntity("M006", "主实体6", null, firstLevelItems6);
// 将所有测试数据添加到返回列表
mainEntities.add(mainEntity1);
mainEntities.add(mainEntity2);
mainEntities.add(mainEntity3);
mainEntities.add(mainEntity4);
mainEntities.add(mainEntity5);
mainEntities.add(mainEntity6);
return mainEntities;
}
/**
* 增强的嵌套列表导出接口 - 支持各种空集合情况
* 使用@GetMapping注解映射HTTP GET请求[6,7](@ref)
* 使用ResponseEntity<byte[]>返回文件下载响应,支持精确控制HTTP响应头[6](@ref)
*
* @param response HttpServletResponse对象,用于设置响应参数
* @return ResponseEntity包含Excel文件的字节数组和下载头信息
* @throws IOException 当文件操作出现IO异常时抛出
*/
@GetMapping("/nested-list")
public ResponseEntity<byte[]> exportNestedList(HttpServletResponse response) throws IOException {
// 生成测试数据
List<MainEntity> dataList = generateTestData();
// 创建SXSSFWorkbook,使用流式处理避免内存溢出(100行缓存)
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
Sheet sheet = workbook.createSheet("嵌套列表");
// 创建单元格样式
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
// 创建表格标题和表头
createTableHeader(sheet, headerStyle);
// 存储合并区域信息
List<CellRangeAddress> mergeRegions = new ArrayList<>();
int rowIndex = 3; // 数据从第4行开始(前3行被表头占用)
// 遍历所有主实体数据
for (MainEntity mainEntity : dataList) {
int mainEntityStartRow = rowIndex; // 记录当前主实体的起始行
int mainEntityRowCount = 0; // 记录当前主实体占用的行数
// 关键修复:安全获取一级子项列表,使用Optional避免空指针异常[1](@ref)
List<FirstLevelItem> firstLevelItems = Optional.ofNullable(mainEntity.getFirstLevelItems())
.orElse(Collections.emptyList());
// 处理空一级子项列表的情况 - 为主实体创建一行基础数据
if (firstLevelItems.isEmpty()) {
Row row = sheet.createRow(rowIndex);
createMainEntityRow(row, mainEntity, dataStyle);
// 为其他列创建空单元格,保持表格结构完整
for (int col = 2; col <= 8; col++) {
Cell emptyCell = row.createCell(col);
emptyCell.setCellValue("");
emptyCell.setCellStyle(dataStyle);
}
mainEntityRowCount = 1;
rowIndex++;
} else {
// 处理有一级子项的情况
for (FirstLevelItem firstItem : firstLevelItems) {
// 关键修复:安全获取二级子项列表,处理null情况
List<SecondLevelItem> secondItems = Optional.ofNullable(firstItem.getSecondLevelItems())
.orElse(Collections.emptyList());
// 计算当前一级子项需要的行数(至少1行)
int firstLevelRows = Math.max(1, secondItems.size());
int firstLevelStartRow = rowIndex;
// 创建当前一级子项的所有数据行
for (int j = 0; j < firstLevelRows; j++) {
Row row = sheet.createRow(rowIndex);
// 处理主实体列(只在每组的首行填充)
if (j == 0) {
createMainEntityCells(row, mainEntity, dataStyle);
}
// 处理一级子项列(只在每组的首行填充)
if (j == 0) {
createFirstLevelCells(row, firstItem, dataStyle);
}
// 处理二级子项列
if (j < secondItems.size()) {
SecondLevelItem secondItem = secondItems.get(j);
createSecondLevelCells(row, secondItem, dataStyle);
} else {
// 如果没有二级子项数据,创建空单元格保持表格对齐
for (int k = 4; k <= 7; k++) {
Cell emptyCell = row.createCell(k);
emptyCell.setCellValue("");
emptyCell.setCellStyle(dataStyle);
}
}
// I列:一级子项数量(每行都填充)
Cell firstLevelCountCell = row.createCell(8);
firstLevelCountCell.setCellValue(firstItem.getFirstLevelCount() != null ?
firstItem.getFirstLevelCount() : 0);
firstLevelCountCell.setCellStyle(dataStyle);
rowIndex++; // 移动到下一行
}
// 如果当前一级子项有多行数据,添加合并区域信息
if (firstLevelRows > 1) {
addFirstLevelMergeRegions(mergeRegions, firstLevelStartRow, firstLevelRows);
}
mainEntityRowCount += firstLevelRows; // 累计主实体行数
}
}
// 如果主实体占用多行,添加合并区域信息
if (mainEntityRowCount > 1) {
addMainEntityMergeRegions(mergeRegions, mainEntityStartRow, mainEntityRowCount);
}
}
// 应用所有合并区域到工作表
for (CellRangeAddress region : mergeRegions) {
sheet.addMergedRegion(region);
}
// 自动调整列宽
autoSizeColumnsWithTracking(sheet, 10);
// 将工作簿写入字节数组输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
workbook.dispose(); // 清理临时文件
// 设置HTTP响应头,触发文件下载[6](@ref)
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=nested_list_export.xlsx");
headers.add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// 返回包含Excel文件的响应实体
return ResponseEntity.ok()
.headers(headers)
.body(baos.toByteArray());
}
/**
* 创建主实体单元格(A列、B列、J列)
* 这些单元格在合并区域中会被垂直合并
*
* @param row 当前行对象
* @param mainEntity 主实体数据
* @param dataStyle 数据单元格样式
*/
private void createMainEntityCells(Row row, MainEntity mainEntity, CellStyle dataStyle) {
// A列:主实体ID
Cell mainIdCell = row.createCell(0);
mainIdCell.setCellValue(mainEntity.getMainId() != null ? mainEntity.getMainId() : "");
mainIdCell.setCellStyle(dataStyle);
// B列:实体名
Cell mainNameCell = row.createCell(1);
mainNameCell.setCellValue(mainEntity.getMainName() != null ? mainEntity.getMainName() : "");
mainNameCell.setCellStyle(dataStyle);
// J列:主实体创建时间,格式化为yyyy-MM-dd字符串
Cell createTimeCell = row.createCell(9);
if (mainEntity.getCreateTime() != null) {
createTimeCell.setCellValue(mainEntity.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
} else {
createTimeCell.setCellValue("");
}
createTimeCell.setCellStyle(dataStyle);
}
/**
* 创建一级子项单元格(C列、D列)
* 这些单元格在合并区域中会被垂直合并
*
* @param row 当前行对象
* @param firstItem 一级子项数据
* @param dataStyle 数据单元格样式
*/
private void createFirstLevelCells(Row row, FirstLevelItem firstItem, CellStyle dataStyle) {
// C列:一级子项ID
Cell firstIdCell = row.createCell(2);
firstIdCell.setCellValue(firstItem.getFirstId() != null ? firstItem.getFirstId() : "");
firstIdCell.setCellStyle(dataStyle);
// D列:一级子项名称
Cell firstNameCell = row.createCell(3);
firstNameCell.setCellValue(firstItem.getFirstName() != null ? firstItem.getFirstName() : "");
firstNameCell.setCellStyle(dataStyle);
}
/**
* 创建二级子项单元格(E列、F列、G列、H列)
* 这些单元格不会被合并,每行都有独立数据
*
* @param row 当前行对象
* @param secondItem 二级子项数据
* @param dataStyle 数据单元格样式
*/
private void createSecondLevelCells(Row row, SecondLevelItem secondItem, CellStyle dataStyle) {
// E列:二级子项ID
Cell secondIdCell = row.createCell(4);
secondIdCell.setCellValue(secondItem.getSecondId() != null ? secondItem.getSecondId() : "");
secondIdCell.setCellStyle(dataStyle);
// F列:二级子项名称
Cell secondNameCell = row.createCell(5);
secondNameCell.setCellValue(secondItem.getSecondName() != null ? secondItem.getSecondName() : "");
secondNameCell.setCellStyle(dataStyle);
// G列:二级子项数量,处理null值
Cell secondCountCell = row.createCell(6);
if (secondItem.getSecondCount() != null) {
secondCountCell.setCellValue(secondItem.getSecondCount());
} else {
secondCountCell.setCellValue(0);
}
secondCountCell.setCellStyle(dataStyle);
// H列:二级子项年份,处理null值
Cell secondYearCell = row.createCell(7);
if (secondItem.getSecondYear() != null) {
secondYearCell.setCellValue(secondItem.getSecondYear());
} else {
secondYearCell.setCellValue("");
}
secondYearCell.setCellStyle(dataStyle);
}
/**
* 创建完整的主实体行(用于空一级子项的情况)
* 当一级子项为空时,只为最外层主实体创建一行数据
*
* @param row 当前行对象
* @param mainEntity 主实体数据
* @param dataStyle 数据单元格样式
*/
private void createMainEntityRow(Row row, MainEntity mainEntity, CellStyle dataStyle) {
createMainEntityCells(row, mainEntity, dataStyle);
}
/**
* 添加一级子项合并区域
* 当一级子项对应多个二级子项时,需要合并一级子项的单元格
*
* @param mergeRegions 合并区域列表
* @param startRow 起始行索引
* @param rowCount 需要合并的行数
*/
private void addFirstLevelMergeRegions(List<CellRangeAddress> mergeRegions, int startRow, int rowCount) {
if (rowCount > 1) {
// 合并C列:一级子项ID
mergeRegions.add(new CellRangeAddress(startRow, startRow + rowCount - 1, 2, 2));
// 合并D列:一级子项名称
mergeRegions.add(new CellRangeAddress(startRow, startRow + rowCount - 1, 3, 3));
// 合并I列:一级子项数量
mergeRegions.add(new CellRangeAddress(startRow, startRow + rowCount - 1, 8, 8));
}
}
/**
* 添加主实体合并区域
* 当主实体有多个一级子项时,需要合并主实体的单元格
*
* @param mergeRegions 合并区域列表
* @param startRow 起始行索引
* @param rowCount 需要合并的行数
*/
private void addMainEntityMergeRegions(List<CellRangeAddress> mergeRegions, int startRow, int rowCount) {
if (rowCount > 1) {
// 合并A列:主实体ID
mergeRegions.add(new CellRangeAddress(startRow, startRow + rowCount - 1, 0, 0));
// 合并B列:实体名
mergeRegions.add(new CellRangeAddress(startRow, startRow + rowCount - 1, 1, 1));
// 合并J列:主实体创建时间
mergeRegions.add(new CellRangeAddress(startRow, startRow + rowCount - 1, 9, 9));
}
}
/**
* 创建表头单元格样式
* 使用粗体、居中显示,增强可读性
*
* @param workbook 工作簿对象
* @return 配置好的单元格样式
*/
private CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true); // 设置粗体
font.setFontHeightInPoints((short) 12); // 设置字体大小
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
return style;
}
/**
* 创建数据单元格样式
* 使用居中对齐,保持数据整洁
*
* @param workbook 工作簿对象
* @return 配置好的单元格样式
*/
private CellStyle createDataStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
return style;
}
/**
* 创建表格标题和表头
* 使用三级表头结构,清晰展示数据层级关系
*
* @param sheet 工作表对象
* @param headerStyle 表头单元格样式
*/
private void createTableHeader(Sheet sheet, CellStyle headerStyle) {
// 第一行表头:大标题(合并A-J列)
Row row1 = sheet.createRow(0);
Cell titleCell = row1.createCell(0);
titleCell.setCellValue("嵌套列表导出");
titleCell.setCellStyle(headerStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 9));
// 第二行表头:分组标题(展示数据层级关系)
Row row2 = sheet.createRow(1);
// 主实体分组(A-B列)
Cell mainEntityHeader1 = row2.createCell(0);
mainEntityHeader1.setCellValue("主实体");
mainEntityHeader1.setCellStyle(headerStyle);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1));
// 一级子项分组(C-D列)
Cell firstLevelHeader1 = row2.createCell(2);
firstLevelHeader1.setCellValue("一级子项列表");
firstLevelHeader1.setCellStyle(headerStyle);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 2, 3));
// 二级子项分组(E-H列)
Cell secondLevelHeader = row2.createCell(4);
secondLevelHeader.setCellValue("二级子项列表");
secondLevelHeader.setCellStyle(headerStyle);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 4, 7));
// 一级子项数量(I列)
Cell firstLevelHeader2 = row2.createCell(8);
firstLevelHeader2.setCellValue("一级子项列表");
firstLevelHeader2.setCellStyle(headerStyle);
// 主实体创建时间(J列)
Cell mainEntityHeader2 = row2.createCell(9);
mainEntityHeader2.setCellValue("主实体");
mainEntityHeader2.setCellStyle(headerStyle);
// 第三行表头:具体列名
Row row3 = sheet.createRow(2);
String[] columnHeaders = {
"主实体ID", "实体名", "一级子项", "一级子项名称",
"二级子项ID", "二级子项名称", "二级子项数量", "二级子项年份",
"一级子项数量", "主实体创建时间"
};
// 为每个列创建表头单元格
for (int i = 0; i < columnHeaders.length; i++) {
Cell headerCell = row3.createCell(i);
headerCell.setCellValue(columnHeaders[i]);
headerCell.setCellStyle(headerStyle);
}
}
/**
* 自动调整列宽(增强版)
* 针对SXSSFWorkbook的特殊处理,避免自动调整列宽时的性能问题
*
* @param sheet 工作表对象
* @param columnCount 需要调整的列数
*/
private void autoSizeColumnsWithTracking(Sheet sheet, int columnCount) {
// 针对SXSSFSheet的特殊处理:跟踪列以支持自动调整列宽
if (sheet instanceof SXSSFSheet) {
SXSSFSheet sxsSheet = (SXSSFSheet) sheet;
for (int i = 0; i < columnCount; i++) {
sxsSheet.trackColumnForAutoSizing(i);
}
}
// 调整每一列的宽度
for (int i = 0; i < columnCount; i++) {
int minWidth = 2000; // 设置最小列宽(约2个字符)
sheet.setColumnWidth(i, minWidth);
sheet.autoSizeColumn(i); // 自动调整列宽
int currentWidth = sheet.getColumnWidth(i);
// 确保列宽不小于最小值
if (currentWidth < minWidth) {
sheet.setColumnWidth(i, minWidth);
currentWidth = minWidth;
}
// 增加10%的边距,避免内容过于拥挤
int newWidth = (int)(currentWidth * 1.1);
if (newWidth < 255 * 256) { // Excel最大列宽限制
sheet.setColumnWidth(i, newWidth);
}
}
}
/**
* 测试数据接口 - 返回增强的测试数据信息
* 使用@GetMapping注解映射HTTP GET请求[7](@ref)
*
* @return 包含测试数据和统计信息的Map对象
*/
@GetMapping("/test-data")
public Map<String, Object> getTestData() {
List<MainEntity> data = generateTestData();
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "成功 - 增强的空集合处理版本");
result.put("data", data);
result.put("count", data.size());
// 统计各种边界情况的行数,用于验证导出逻辑
int totalRows = 0;
int nullFirstLevelCount = 0;
int emptyFirstLevelCount = 0;
int nullSecondLevelCount = 0;
int emptySecondLevelCount = 0;
// 遍历数据统计各种边界情况
for (MainEntity entity : data) {
if (entity.getFirstLevelItems() == null) {
nullFirstLevelCount++;
totalRows += 1; // 即使一级子项为null,也会创建一行
} else if (entity.getFirstLevelItems().isEmpty()) {
emptyFirstLevelCount++;
totalRows += 1; // 即使一级子项为空列表,也会创建一行
} else {
for (FirstLevelItem firstItem : entity.getFirstLevelItems()) {
if (firstItem.getSecondLevelItems() == null) {
nullSecondLevelCount++;
totalRows += 1;
} else if (firstItem.getSecondLevelItems().isEmpty()) {
emptySecondLevelCount++;
totalRows += 1;
} else {
totalRows += firstItem.getSecondLevelItems().size();
}
}
}
}
// 添加统计信息到返回结果
result.put("totalRows", totalRows);
result.put("excelColumns", 10);
result.put("nullFirstLevelEntities", nullFirstLevelCount);
result.put("emptyFirstLevelEntities", emptyFirstLevelCount);
result.put("nullSecondLevelItems", nullSecondLevelCount);
result.put("emptySecondLevelItems", emptySecondLevelCount);
result.put("features", "支持所有空集合情况:一级为null、一级为空列表、二级为null、二级为空列表");
return result;
}
}