使用EasyPoi完成復(fù)雜一對多excel表格導(dǎo)出功能全過程
業(yè)務(wù)需求
從一個簡單的倉庫業(yè)務(wù)說起,倉庫業(yè)務(wù),會有進庫記錄,會有出庫記錄,會有庫存,客戶的需求就是需要一個庫存盤點單,盤點單通俗來講:將庫存中每個商品的出入庫記錄都統(tǒng)計出來,看看每個商品出過多少貨物,入過多少貨物,本月庫存多少,上月庫存多少。
需求難點
一個貨物會出過多次貨物,入過多次貨物,導(dǎo)出的 excel 就要做成 一對多 格式的導(dǎo)出
簡單舉例:
啤酒:入庫2次,出庫3次,最終體現(xiàn)在 excel 中效果如下圖:

通過 EasyPoi 實現(xiàn)需求
EasyPoi 文檔地址:http://doc.wupaas.com/docs/easypoi/easypoi-1c0u4mo8p4ro8
SpringBoot 使用:
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.2.0</version>
</dependency>
Gradle 使用:
implementation 'cn.afterturn:easypoi-base:4.2.0' implementation 'cn.afterturn:easypoi-annotation:4.2.0' implementation 'cn.afterturn:easypoi-web:4.2.0'
使用 EasyPoi 提供的注解,自定義導(dǎo)出類模板
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 導(dǎo)出 excel 模板類
*/
@Getter
@Setter
public class ExportTemplate implements Serializable {
@Excel(name = "序號", needMerge = true, type = 10)
private int index;
@Excel(name = "商品名稱", needMerge = true, width = 30.0)
private String goodName;
@Excel(name = "商品單位", needMerge = true)
private String goodUnit;
@Excel(name = "上月庫存數(shù)量", needMerge = true, type = 10)
private Integer lastMonthSurplusNum;
@Excel(name = "本月庫存數(shù)量", needMerge = true, type = 10)
private Integer thisMonthSurplusNum;
@ExcelCollection(name = "本月入庫信息")
private List<GoodInItem> goodInItems;
@ExcelCollection(name = "本月出庫信息")
private List<GoodOutItem> goodOutItems;
@Excel(name = "備注", needMerge = true, width = 30.0)
private String remark;
/**
* 入庫信息
*/
@Getter
@Setter
public static class GoodInItem {
@Excel(name = "入庫日期", exportFormat = "yyyy-MM-dd", width = 20.50)
private Date purchaseDate;
@Excel(name = "入庫號", width = 25.50)
private String purchaseNum;
@Excel(name = "入庫單價", type = 10)
private BigDecimal unitPrice;
@Excel(name = "入庫數(shù)量", type = 10)
private Integer totalNum;
}
/**
* 出庫信息
*/
@Getter
@Setter
public static class GoodOutItem {
@Excel(name = "出庫日期", exportFormat = "yyyy-MM-dd", width = 20.50)
private Date outDate;
@Excel(name = "出庫號", width = 25.50)
private String sellNum;
@Excel(name = "出庫數(shù)量", type = 10)
private Integer totalNum;
@Excel(name = "成本金額", type = 10)
private BigDecimal priceIn;
@Excel(name = "銷售金額", type = 10)
private BigDecimal priceOut;
}
}
實體類中使用的注解作用解釋:
- 1.@Getter lombok 注解,用于給所有屬性提供 getter 方法
- 2.@Setter lombok 注解,用于給所有屬性提供 setter 方法
- 3.@Excel easypoi 注解,name 就等于導(dǎo)出 excel 的列名稱,width 就是寬度,type 就是這個屬性的類型,1表示文本,默認也是文本,10就是數(shù)字,needMerge 表示是否縱向合并單元格,也就是上下列合并
- 4.@ExcelCollection easypoi 注解,name 就等于導(dǎo)出 excel 的列名稱,被此注解標注的集合,就等于在其列下面創(chuàng)建對等數(shù)量的行,就類似于這種

最后模板弄好之后,就可以通過easypoi 的工具類來導(dǎo)出,easypoi 推薦的導(dǎo)出工具類如下:

這個方法的三個參數(shù)表示含義解釋:
ExportParams:參數(shù)表示Excel 導(dǎo)出參數(shù)設(shè)置類,easypoi 自定義的類pojoClass:你要導(dǎo)出的類模板dataSet:數(shù)據(jù)集合
具體實現(xiàn)
@GetMapping(value = "export")
public void export(HttpServletRequest req, HttpServletResponse resp) {
List<ExportTemplate> exportData = new ArrayList();
// 步驟1:構(gòu)建要導(dǎo)出excel的數(shù)據(jù)集合
for (int i = 0; i < 5; i++) {
ExportTemplate data = new ExportTemplate();
data.setIndex(i);
data.setGoodName("測試商品");
data.setGoodUnit("瓶");
data.setLastMonthSurplusNum(5); // 上月庫存
data.setThisMonthSurplusNum(3); // 本月庫存
// ... 剩下的就是類似的加值
exportData.add(data);
}
try {
// 步驟2:開始導(dǎo)出 excel
ExportParams params = new ExportParams();
params.setTitle("庫存盤點單標題");
params.setSheetName("庫存盤點單工作表名稱");
params.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(params, ExportTemplate.class, exportData);
String nowStr = DateTimeFormatter.ofPattern(LocalDateTime.now()).format("yyyyMMddHHmm"); // 時間串
String fileName = nowStr + "_庫存盤點單"; // 文件名稱
String tempDir = "C:/Users/huxim/Downloads";
File filePath = new File(tempDir + File.separator);
if (!filePath.exists()) filePath.mkdirs(); // 如果文件目錄不存在就創(chuàng)建這個目錄
FileOutputStream fos = new FileOutputStream(tempDir + File.separator + fileName);
workbook.write(fos);
fos.close();
resp.setContentType("application/octet-stream");
resp.setCharacterEncoding("utf-8");
response.addHeader("Content-disposition", "attachment; filename="
+ this.makeDownloadFileName(req, fileName));
IOUtils.copy(new FileInputStream(tempFile), response.getOutputStream());
System.out.println("導(dǎo)出成功~~~");
} catch (Exception e) {
throw new RuntimeException("導(dǎo)出 excel 失敗~~~");
}
}
/**
* 判斷是否是 IE 瀏覽器
* 返回對應(yīng)的字符串格式
*/
public static String makeDownloadFileName(HttpServletRequest request, String fileName) {
String agent = request.getHeader("User-Agent");
byte[] bytes = fileName.getBytes(StandardCharsets.UTF_8);
if (agent.contains("MSIE") || agent.contains("Trident") || agent.contains("Edge")) {
// IE
return new String(bytes, StandardCharsets.UTF_8);
} else {
return new String(bytes, StandardCharsets.ISO_8859_1);
}
}
導(dǎo)出成功后的excel 就類似于如下這種:

總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java 中的 Class.forName(類名) 使用及原理解析
Class.forName是Java中用于動態(tài)加載類的強大工具,廣泛應(yīng)用于數(shù)據(jù)庫驅(qū)動加載、反射機制和插件系統(tǒng)等場景,它通過ClassLoader加載類并執(zhí)行靜態(tài)初始化代碼,但在使用時需要注意類路徑、初始化副作用和類加載器的選擇等問題,感興趣的朋友一起看看吧2024-12-12
Java無需解壓直接讀取ZIP壓縮包里的文件及內(nèi)容
最近開發(fā)的時候遇到要獲取到zip壓縮包里面的文件內(nèi)容,解決方案就是通過ZipInputStream來讀取,下面通過實例代碼介紹Java無需解壓直接讀取ZIP壓縮包里的文件及內(nèi)容,感興趣的朋友跟隨小編一起看看吧2024-03-03
重學(xué)SpringBoot3之日志Logging使用方式
在日常開發(fā)中會遇到不同的異常,日志方便我們?nèi)ヅ挪樘幚?這篇文章主要給大家介紹了關(guān)于重學(xué)SpringBoot3之日志Logging使用方式的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-06-06
詳解如何把Java中if-else代碼重構(gòu)成高質(zhì)量代碼
這篇文章主要介紹了詳解如何把Java中if-else代碼重構(gòu)成高質(zhì)量代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java雜談之類和對象 封裝 構(gòu)造方法以及代碼塊詳解
在現(xiàn)實世界中,真實存在的東西,比如吉普車,卡丁車,貨車。我們在認識它的時候就會在腦海中將它抽象為一種類別叫做車。 好了,那再計算機世界中,它同樣的也會這樣做2021-09-09

