用自定義注解解決excel動態表頭導出的問題

導入的excel有固定表頭+動態表頭如何解決

自定義注解:

import java.lang.annotation.*;/*** 自定義注解,用于動態生成excel表頭*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldLabel {// 字段中文String label();// 字段順序int order() default 0;// 分組標識String group() default "default";}

導出的類:


import cn.com.fsg.ihro.openacct.support.annotation.FieldLabel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;/*** 服務費收入項明細excel導出** @author makejava* @since 2025-07-01 16:41:21*/
@Data
public class ExcelVO {@FieldLabel(label = "錯誤信息", order = 1, group = "error")private String errMsg;@FieldLabel(label = "業務日期", order = 11, group = "export")private String businessDay; @FieldLabel(label = "雇員姓名", order = 15, group = "export")private String empName;@FieldLabel(label = "證件類型", order = 16, group = "export")private String idType;@FieldLabel(label = "證件號碼", order = 17, group = "export")private String idNumber;@Schema(description = "動態字段:服務費收入項-產品方案名稱")private Map<String, BigDecimal> dynamicMap = new HashMap<>();}

工具里:利用反射+自定義注解+泛型 解析ExcelVO 并導出文件


import cn.com.fsg.ihro.openacct.support.annotation.FieldLabel;
import com.alibaba.excel.EasyExcel;
import org.springframework.util.CollectionUtils;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;/*** Excel導出工具類*/
public class ExcelExportUtil {/*** 導出Excel(自動識別固定列 + 動態列)** @param response 響應對象* @param dataList 數據列表* @param fileName 文件名* @param groups   分組*/public static <T> void export(HttpServletResponse response, List<T> dataList, String fileName, String... groups) throws IOException {if (CollectionUtils.isEmpty(dataList)) {return;}// 提取動態表頭 mapSet<String> dynamicHeaders = extractDynamicHeaders(dataList.get(0));// 構建表頭:固定表頭+動態表頭List<List<String>> head = buildHead(dataList.get(0).getClass(), dynamicHeaders, groups);// 構建數據List<Map<Integer, Object>> content = convertToMapList(dataList, groups);// 設置響應頭response.setContentType("application/vnd.ms-excel;charset=UTF-8");response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));// 執行導出EasyExcel.write(response.getOutputStream()).head(head).sheet().doWrite(content);}/*** 提取動態列頭(假設 VO 中有一個 Map 字段用于存儲動態列)** @param vo 數據對象*/private static Set<String> extractDynamicHeaders(Object vo) {// getDeclaredFields獲取當前類中聲明的所有字段for (Field field : vo.getClass().getDeclaredFields()) {// 判斷當前字段的類型是否是 Map 類型或其子類if (Map.class.isAssignableFrom(field.getType())) {try {field.setAccessible(true);@SuppressWarnings("unchecked")Map<String, Object> dynamicMap = (Map<String, Object>) field.get(vo);return dynamicMap != null ? dynamicMap.keySet() : Collections.emptySet();} catch (IllegalAccessException ignored) {}}}return Collections.emptySet();}/*** 構建表頭:固定表頭+動態表頭(從 VO 的 @FieldLabel 注解提取)** @param excelClass     Excel類* @param dynamicHeaders 動態列* @param groups         分組*/public static List<List<String>> buildHead(Class<?> excelClass, Set<String> dynamicHeaders, String... groups) {List<List<String>> head = new ArrayList<>();// 1、獲取當前類中聲明的所有字段 2、字段有FieldLabel注解 3、通過group過濾 4、通過order排序List<Field> sortedFields = Arrays.stream(excelClass.getDeclaredFields()).filter(f -> f.isAnnotationPresent(FieldLabel.class)).filter(f -> groups.length == 0 || Arrays.asList(groups).contains(f.getAnnotation(FieldLabel.class).group())).sorted(Comparator.comparingInt(f -> f.getAnnotation(FieldLabel.class).order())).collect(Collectors.toList());// 構建固定列頭for (Field field : sortedFields) {FieldLabel annotation = field.getAnnotation(FieldLabel.class);head.add(Collections.singletonList(annotation.label()));}// 添加動態列頭for (String header : dynamicHeaders) {head.add(Collections.singletonList(header));}return head;}/*** 構建數據:將VO 轉換為 Map<Integer, Object>** @param dataList 數據* @param groups   分組*/public static <T> List<Map<Integer, Object>> convertToMapList(List<T> dataList, String... groups) {return dataList.stream().map(vo -> {Map<Integer, Object> row = new LinkedHashMap<>();int index = 0;// 1、獲取當前類中聲明的所有字段 2、字段有FieldLabel注解 3、通過group過濾 4、通過order排序List<Field> sortedFields = Arrays.stream(vo.getClass().getDeclaredFields()).filter(f -> f.isAnnotationPresent(FieldLabel.class)).filter(f -> groups.length == 0 || Arrays.asList(groups).contains(f.getAnnotation(FieldLabel.class).group())).sorted(Comparator.comparingInt(f -> f.getAnnotation(FieldLabel.class).order())).collect(Collectors.toList());// 固定字段for (Field field : sortedFields) {field.setAccessible(true);try {row.put(index++, field.get(vo));} catch (IllegalAccessException e) {throw new RuntimeException("字段讀取失敗:" + field.getName(), e);}}// 動態字段for (Object value : extractDynamicMap(vo)) {row.put(index++, value);}return row;}).collect(Collectors.toList());}/*** 提取 VO 中的動態字段(支持泛型)** @param vo 數據對象*/private static List<Object> extractDynamicMap(Object vo) {// getDeclaredFields獲取當前類中聲明的所有字段for (Field field : vo.getClass().getDeclaredFields()) {// 判斷當前字段的類型是否是 Map 類型或其子類if (Map.class.isAssignableFrom(field.getType())) {field.setAccessible(true);try {@SuppressWarnings("unchecked")Map<String, Object> map = (Map<String, Object>) field.get(vo);if (map != null) {return new ArrayList<>(map.values());}} catch (IllegalAccessException ignored) {}}}return Collections.emptyList();}
}

用法示例:

 	@GetMapping("/import-download")@Operation(summary = "下載excel")public void importDownload(@Valid SerIncomeDetailExcelQueryReqVO reqVO, HttpServletResponse response) throws IOException {// Step 1: 調用 service 查詢數據,并轉換為 ExcelVO 數據List<ExcelVO> dataList = service.importDownload(reqVO);// Step 2: 使用 EasyExcel 導出ExcelExportUtil.export(response, dataList, "服務費收入項明細-金額導入.xlsx", "export", "error");}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87495.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87495.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87495.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android-EDLA 解決 GtsMediaRouterTestCases 存在 fail

問題描述&#xff1a;[原因]R10套件新增模塊&#xff0c;getRemoteDevice獲取遠程藍牙設備時&#xff0c;藍牙MAC為空 [對策]實際藍牙MAC非空;測試時繞過處理 1.release/ebsw_skg/skg/frameworks/base/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManage…

雙渦輪增壓器結構設計cad【5張】+設計說明書

摘要 隨著汽車制造商和消費者對動力性能的要求不斷增加&#xff0c;發動機需要在更寬的轉速范圍內提供更大的功率和扭矩。雙渦輪增壓器可以幫助實現這一目標&#xff0c;通過在不同轉速下調整渦輪的工作狀態來提供更平順的動力輸出。單一渦輪增壓器可能存在渦輪滯后和增壓延遲…

大數據輕量化流批一體架構探索實踐(一)

最近學習了解到一種輕量化&#xff0c;維護門檻較低的流批一體化的架構方式&#xff0c;雖然目前還是不太成熟&#xff0c;自己也在探索學習中。 dolphinschedulerdinkystarrocksflinkzookeper 后面我會逐步發一下這個整體架構的特點&#xff0c;以及各個組件作用&#xff0c;和…

【2025/07/04】GitHub 今日熱門項目

GitHub 今日熱門項目 &#x1f680; 每日精選優質開源項目 | 發現優質開源項目&#xff0c;跟上技術發展趨勢 &#x1f4cb; 報告概覽 &#x1f4ca; 統計項&#x1f4c8; 數值&#x1f4dd; 說明&#x1f4c5; 報告日期2025-07-04 (周五)GitHub Trending 每日快照&#x1f55…

HarmonyOS學習記錄3

HarmonyOS學習記錄3 本文為個人學習記錄&#xff0c;僅供參考&#xff0c;如有錯誤請指出。本文主要記錄ArkTS基礎語法&#xff0c;僅記錄了部分我覺得與其他語言不太類似的地方&#xff0c;具體規范請參考官方文檔。 參考官方文檔&#xff1a;https://developer.huawei.com/co…

HKS201-M24 大師版 8K60Hz USB 3.0 適用于 2 臺 PC 1臺顯示器 無縫切換 KVM 切換器

HKS201-M24 8K60Hz HDMI 2.1 2x1 KVM 切換器&#xff0c;適用于 2 臺 PC&#xff0c;帶 EDID 仿真、千兆 LAN、雙充電和 USB 3.2 Gen 1 HKS201-M24 產品概述 TESmart 重新定義智能工作空間&#xff0c;無縫雙PC控制。 真正的 8K60Hz 亮度&#xff0c;具有 EDID 穩定性和超快速…

stm32f103vct6的DAC口的輸出電壓達不到3.3V

問題&#xff1a;調試時發現自己設置的DAC在最大時達不到3.3V&#xff0c;總結了原因&#xff0c;記錄下。 原因&#xff1a;使用時&#xff0c;注意有沒有其他負載&#xff0c;有的話最好給負載獨立供電&#xff0c;不要只用STM32f103的板湊活著供電&#xff0c;我的就是這個…

java8 Collectors.mapping 使用 例子 學習

java8 Collectors.mapping 使用 例子 學習 Map<String, List<String>> colorApple appleList.stream().collect(Collectors.groupingBy(Apple::getColor, Collectors.mapping(Apple::getVariety, toList()))); colorApple.forEach((k, v) -> {System.out.prin…

動態規劃-P1216 [IOI 1994] 數字三角形 Number Triangles

P1216 [IOI 1994] 數字三角形 Number Triangles 題目來源-洛谷題庫 思路 如果用貪心只是找當前的到達該點的路徑最大值&#xff0c;可能結果無法做到最優最值問題試著看能否將大問題分解成若干個小問題 走到a[i] [j ]這個點的最值來源于上一步a[i-1 ] [j]和a[i-1] [j-1]的最…

25年Java后端社招技術場景題!

一、電商領域高頻場景題1. 百萬級QPS秒殺系統設計場景需求&#xff1a;設計一個支持百萬級QPS的秒殺系統&#xff0c;要求解決超賣問題&#xff0c;保證系統高可用。技術方案&#xff1a;分層削峰&#xff1a;前端頁面靜態化按鈕防重復點擊Redis集群&#xff1a;采用Lua腳本實現…

牛客:HJ16 購物單【01背包】【華為機考】

學習要點 深入理解回溯深入理解01背包問題 題目鏈接 購物單_牛客題霸_牛客網 題目描述 解法1&#xff1a;回溯 其實此題非常符合取子集的邏輯&#xff0c;但是時間復雜度太高。通過11/14。想寫出來這個回溯過程&#xff0c;不容易。 #include <iostream> #include &l…

[學習記錄]Unity毛發渲染[URP]-Fin基礎版

鰭片法是一種在多邊形表面垂直添加許多多邊形&#xff0c;并在其上粘貼毛發紋理以營造毛茸茸的感覺的技術。這就像種植許多鰭&#xff08;就像魚身上的鰭一樣&#xff09;。本期我將在Unity6中實現一下基礎的Fin毛發&#xff0c;并不涉及光照著色。后面我會出一篇加上著色效果的…

指針篇(7)- 指針運算筆試題(阿里巴巴)

目錄 一、指針運算筆試題解析3.1 題目1&#xff1a;3.2 題目2&#xff1a;3.3 指針3&#xff1a;3.4 題目4&#xff1a;3.5 題目5&#xff1a;3.6 題目6&#xff1a;3.7 題目7&#xff1a; 總結 一、指針運算筆試題解析 3.1 題目1&#xff1a; #include<stdio.h> int m…

homebrew的一些常用方法

前言 因本人工作換到mac電腦&#xff0c;對包管理器homebrew的需求增加&#xff0c;因此將一些常用命令做如下記錄&#xff0c;本博客主要用作記錄用。 官網 macOS&#xff08;或 Linux&#xff09;缺失的軟件包的管理器 — Homebrew 常用命令 如果腳本因網絡問題無法下載…

【Python面試題】Python面試之基礎知識常見面試題3-匯總篇(精選30個)

目錄專欄導讀前言1. 字典的內存管理機制是什么&#xff1f;2. 列表的內存管理機制是什么&#xff1f;3. 元組和列表的區別4. 字符串插值的方法5. 閉包、裝飾器的原理閉包&#xff08;Closure&#xff09;裝飾器&#xff08;Decorator&#xff09;6. map、filter的區別7. range(…

【免費.NET方案】CSV到PDF與DataTable的快速轉換

CSV作為輕量級數據載體&#xff0c;在數據傳輸中占比超過70%。但其原生格式存在三大痛點&#xff1a; 可視化缺陷&#xff1a;無法直接生成可打印的報表結構限制&#xff1a;缺乏數據類型定義和關系約束安全風險&#xff1a;易被意外修改導致數據失真 因此&#xff0c;我們常…

connect的斷線重連

connect的短線重連 客戶端代碼的編寫服務器代碼的編寫總結 客戶端代碼的編寫 #include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h>…

通過觀看數百個外科手術視頻講座來學習多模態表征|文獻速遞-最新論文分享

Title題目Learning multi-modal representations by watching hundreds of surgical video lectures通過觀看數百個外科手術視頻講座來學習多模態表征01文獻速遞介紹外科計算機視覺領域的最新進展&#xff0c;已開始為手術室&#xff08;OR&#xff09;的新一代人工智能輔助支…

微信小程序如何實現再多個頁面共享數據

在微信小程序中&#xff0c;實現多個頁面共享數據有以下幾種常用方式&#xff0c;根據場景選擇最適合的方案&#xff1a; 全局變量&#xff08;App.js&#xff09; 適用場景&#xff1a;簡單數據共享&#xff08;非響應式&#xff09; 實現方式&#xff1a; javascript // ap…

PCIE5.0 TAG說明(ima回答)

在PCIe 5.0規范中&#xff0c;TLP&#xff08;Transaction Layer Packet&#xff09;報文的Tag字段用于標識和管理事務。以下是關于Tag的生成和使用規則和定義的詳細描述&#xff1a; Tag字段的定義 Tag字段&#xff1a;位于TLP報文的Header中&#xff0c;占用8位&#xff08…