多场景对练数据的 Excel 横向导出方案(EasyExcel 动态表头实践)
一、背景
在日常开发中,我们经常需要将复杂的场景数据导出成 Excel 文件,方便运营或培训人员查看。
比如“客户与学员对练场景”这种数据,原始结构大致如下:
多个场景(List<LearnHouseScene2Vo>)
每个场景里有 若干轮对话(List<SceneItem>)
一个 SceneItem 就是一轮对话:
客户节点
客户话术
学员节点
学员标准话术
关键词
需求:
希望在 Excel 中导出成:
每个场景占 5 列(客户节点、客户话术、学员节点、学员标准话术、关键词)
多个场景横向拼接
每行对应第几轮对话,不足的用空白填充
二、需求分析
举个例子:
场景1 有 2 轮
场景2 有 3 轮
期望的 Excel:
场景1-客户节点 | 场景1-客户话术 | 场景1-学员节点 | 场景1-学员标准话术 | 场景1-关键词 | 场景2-客户节点 | 场景2-客户话术 | 场景2-学员节点 | 场景2-学员标准话术 | 场景2-关键词 |
---|---|---|---|---|---|---|---|---|---|
A1 | A1话术 | S1 | S1话术 | K1 | B1 | B1话术 | S2 | S2话术 | K2 |
A2 | A2话术 | S2 | S2话术 | K2 | B2 | B2话术 | S2 | S2话术 | K2 |
B3 | B3话术 | S3 | S3话术 | K3 |
三、解决方案
动态表头
每个场景占 5 列,表头通过循环生成,使用 EasyExcel 的多级表头功能。数据行构造
先计算所有场景的最大对话轮数
maxRow
遍历
0 ~ maxRow-1
,每一行拼接所有场景的对应对话不足的补空字符串
四、实体类
@Data
public class SceneItem {@ExcelProperty("客户节点")private String customerNode;@ExcelProperty("客户话术")private String customerScript;@ExcelProperty("学员节点")private String studentNode;@ExcelProperty("学员标准话术")private String studentScript;@ExcelProperty("关键词")private String keyword;
}
@Data
public class LearnHouseScene2Vo {// 存储动态数量的场景组private List<SceneItem> sceneItems = new ArrayList<>();/*** 向当前场景组中追加一个对话节点*/public void addSceneItem(SceneItem item) {sceneItems.add(item);}}
五、工具类实现
import com.alibaba.excel.EasyExcel;import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class SceneExcelExporter {/*** 导出场景对练数据到 Excel** @param scenes 多个场景* @param out 输出流*/public static void export(List<LearnHouseScene2Vo> scenes, OutputStream out) {List<List<String>> head = buildHead(scenes);List<List<String>> data = buildData(scenes);EasyExcel.write(out).head(head).sheet("场景对练").doWrite(data);}/*** 构造动态表头*/private static List<List<String>> buildHead(List<LearnHouseScene2Vo> scenes) {List<List<String>> head = new ArrayList<>();for (int i = 0; i < scenes.size(); i++) {int idx = i + 1;head.add(Arrays.asList("场景" + idx, "客户节点"));head.add(Arrays.asList("场景" + idx, "客户话术"));head.add(Arrays.asList("场景" + idx, "学员节点"));head.add(Arrays.asList("场景" + idx, "学员标准话术"));head.add(Arrays.asList("场景" + idx, "关键词"));}return head;}/*** 构造数据行*/private static List<List<String>> buildData(List<LearnHouseScene2Vo> scenes) {int maxRow = scenes.stream().mapToInt(vo -> vo.getSceneItems().size()).max().orElse(0);List<List<String>> rows = new ArrayList<>();for (int r = 0; r < maxRow; r++) {List<String> row = new ArrayList<>();for (LearnHouseScene2Vo vo : scenes) {if (r < vo.getSceneItems().size()) {SceneItem item = vo.getSceneItems().get(r);row.add(item.getCustomerNode());row.add(item.getCustomerScript());row.add(item.getStudentNode());row.add(item.getStudentScript());row.add(item.getKeyword());} else {// 补空白row.add(""); row.add(""); row.add(""); row.add(""); row.add("");}}rows.add(row);}return rows;}
}
六、使用方式
List<LearnHouseScene2Vo> scenes = findUrlGetSPBlockingData3(url); // 解析后的场景数据
try (OutputStream out = new FileOutputStream("场景对练.xlsx")) {SceneExcelExporter.export(scenes, out);
}
七、总结
核心点:多场景横向导出,本质是动态生成表头 + 按行拼接数据。
通用性:这个
SceneExcelExporter
工具类可以复用在任何类似的“多组相同字段横向导出”需求中。扩展点:如果需要在 Excel 顶部加一行大标题,可以在 EasyExcel 写出前用 POI 或者在 head 中加额外层级。