导入的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…