别再手动合并单元格了!若依(RuoYi) 3.5.0导出Excel的合并行功能改造实录
若依(RuoYi)框架下Excel导出功能的深度改造实战
在前后端分离的企业级应用开发中,数据导出功能几乎是每个后台管理系统必备的基础能力。作为国内广泛使用的快速开发框架,若依(RuoYi)提供了开箱即用的Excel导出工具类,但在处理复杂表格样式时仍存在局限性。本文将分享如何基于若依3.5.0版本,对其Excel导出功能进行深度改造,实现智能合并行的高级功能。
1. 原生导出功能的痛点分析
若依框架默认的ExcelUtil工具类虽然能满足基础导出需求,但在实际业务场景中经常遇到需要合并单元格的特殊需求。例如订单明细导出时,同一个订单号的多条商品记录应该合并显示,而传统做法需要开发者在业务代码中手动处理:
// 传统做法示例(伪代码) List<OrderItem> items = orderService.selectItems(); Map<String, List<OrderItem>> grouped = items.stream() .collect(Collectors.groupingBy(OrderItem::getOrderNo)); for(List<OrderItem> group : grouped.values()) { if(group.size() > 1) { // 手动计算合并区域 sheet.addMergedRegion(new CellRangeAddress( startRow, endRow, orderNoColumn, orderNoColumn)); } }这种实现方式存在三个明显缺陷:
- 业务侵入性强:合并逻辑与业务代码耦合
- 维护成本高:每次需求变更都需要修改Java代码
- 扩展性差:无法动态配置合并规则
2. 改造方案设计与技术选型
2.1 核心改造思路
我们决定采用注解驱动的方式改造导出功能,主要设计目标包括:
- 声明式配置:通过
@Excel注解新增mergeLine属性 - 自动合并算法:运行时动态识别相同值行并合并
- 非侵入式扩展:保持与原工具类的兼容性
2.2 关键技术点
改造涉及的关键技术组件:
| 技术组件 | 作用 | 版本要求 |
|---|---|---|
| Apache POI | Excel底层操作 | 4.1.2+ |
| Java反射 | 动态读取注解 | JDK8+ |
| 若依框架 | 基础运行环境 | 3.5.0 |
3. 具体实现步骤
3.1 注解扩展
首先在@Excel注解中新增配置属性:
public @interface Excel { // ...原有属性保持不变 String mergeLine() default ""; // 新增合并配置属性 }3.2 核心算法实现
在ExcelUtil工具类中添加合并行处理逻辑:
public Cell addCell(Excel attr, Row row, T vo, Field field, int column, T vo_previous, int thisLine) { // ...原有单元格创建逻辑 // 合并行处理逻辑 String[] mergeCols = attr.mergeLine().split(","); if(mergeCols.length > 0 && !mergeCols[0].isEmpty()) { if(value.equals(value_previous)) { if(this.mergeLine_start == 0) { this.mergeLine_start = thisLine - 1; } this.mergeLine_end = thisLine; } else { if(this.mergeLine_start != 0 && this.mergeLine_end != 0) { for(String col : mergeCols) { CellRangeAddress region = new CellRangeAddress( this.mergeLine_start, this.mergeLine_end, Integer.parseInt(col), Integer.parseInt(col)); sheet.addMergedRegion(region); } } this.mergeLine_start = 0; this.mergeLine_end = 0; } } return cell; }3.3 使用示例
实体类配置示例:
public class OrderExportVO { @Excel(name = "订单编号", mergeLine = "0,7,8") private String orderNo; @Excel(name = "商品名称") private String productName; // ...其他字段 }Controller层调用方式保持不变:
@GetMapping("/export") public AjaxResult export(Order order) { List<OrderExportVO> list = convertToExportVO(orderService.selectList(order)); return ExcelUtil.exportExcel(list, "订单数据"); }4. 实践中的优化技巧
4.1 性能优化建议
处理大数据量导出时需要注意:
- 使用SXSSFWorkbook:默认采用流式写入模式
- 合理设置合并区间:避免过多小范围合并
- 批量处理数据:推荐每次处理5000条左右
4.2 样式保持方案
合并单元格后可能遇到样式丢失问题,可通过以下方式解决:
// 合并后重新应用样式 RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet, wb); RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet, wb); RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet, wb); RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet, wb);4.3 常见问题排查
- 索引越界异常:确认注解中配置的列号从0开始
- 合并不生效:检查字段值的equals方法实现
- 内存溢出:增加JVM内存参数
-Xmx512m
5. 扩展应用场景
本方案不仅适用于简单的单列合并,通过灵活配置可以实现:
- 多级合并:如先按订单号合并,再按客户合并
- 交叉合并:同时合并行和列的特殊表格
- 条件合并:基于业务规则动态决定是否合并
对于更复杂的导出需求,可以考虑结合模板引擎如Freemarker实现动态模板导出,但这需要权衡开发复杂度与灵活性。
