当前位置: 首页 > news >正文

用自定义注解解决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.lryc.cn/news/578999.html

相关文章:

  • 【全网唯一】自动化编辑器 Windows版纯本地离线文字识别插件
  • 少样本学习在计算机视觉中的应用:原理、挑战与最新突破
  • 分布式事务理论基础及常见解决方案
  • 【科研绘图系列】基于R语言的种质资源评分可视化教程:条形图与地理分布图
  • Redis搭建集群模式
  • 桥岛隧大型工程 3D 可视化监测平台
  • Objective-C 路由表原理详解
  • Git 怎么判断是否冲突?
  • 开源 Python 库---Diffusers 库的安装及运用(自留)
  • Python学习之——单例模式
  • 智慧赋能高压并网:分布式光伏监控系统在5.88MW物流园项目的实践解析
  • PHP Yii2 安装SQL Server扩展-MAC M4 Pro芯片
  • 【AI论文】SPIRAL:零和博弈中的自对弈通过多智能体多轮强化学习激励推理能力
  • 场外交易(OTC)财富管理系统开发及解决方案报告
  • 【Part 3 Unity VR眼镜端播放器开发与优化】第四节|高分辨率VR全景视频播放性能优化
  • 腾讯云认证考试报名 - TDSQL数据库交付运维专家(TCCE MySQL版)
  • 电子电气架构 --- SOVD功能简单介绍
  • 二、jenkins之idea提交项目到gitlab、jenkins获取项目
  • 【NLP第二期中文分词技术:规则、统计与混合方法全解】
  • 设计模式精讲 Day 23:访问者模式(Visitor Pattern)
  • uniapp实现图片预览,懒加载
  • React Native 安卓、苹果、鸿蒙5.0 三端适配方案:条件编译 + 平台适配层
  • 信创版nhdeep档案管理系统单机版在银河麒麟桌面系统安装和使用说明
  • UI前端与数字孪生结合探索:智慧建筑的能耗管理与优化
  • 【论文阅读39】PINN求边坡内时空变化的地震动响应(位移、速度、加速度)场分布
  • npm代理设置 + npm配置镜像源
  • Node.js、npm 与 nvm 使用全指南:安装、版本管理与开发环境配置详解
  • 在 Docker Desktop 使用 Kubernetes
  • RuoYi框架低代码特性
  • 鸿蒙自定义相机的拍照页面