EasyExcel 数据字典转换器实战:注解驱动设计

一、场景痛点与解决方案

1. 问题背景

在 Excel 导入导出场景中,开发者常面临以下问题:

  • 数据可读性差:数据库存储的字典值(如 1、true)直接导出时难以理解
  • 双向转换复杂:导入时需将用户输入的标签反向解析为存储值
  • 代码侵入性强:硬编码字典类型导致业务逻辑与字典管理耦合

2. 设计方案

二、核心实现解析

1. 注解驱动设计


代码示例

java 复制代码
@Data
public class ConfigRespVO {
    // 导出列声明 + 字典类型绑定
    @ExcelProperty(value = "参数类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.CONFIG_TYPE) 
    private Integer type;
}

2. 双向转换流程

导出流程(值 → 标签)
Excel DictConvert DictService 请求转换值(1) getLabel("config_type", 1) "文本类型" 写入单元格 Excel DictConvert DictService

导入流程(标签 → 值)
Excel DictConvert DictService Java 读取单元格("文本类型") parseValue("config_type", "文本类型") "1" 设置Integer类型字段值 Excel DictConvert DictService Java

三、关键技术实现

1. 转换器核心类

java 复制代码
@Slf4j
public class DictConvert implements Converter<Object> {

    @Override
    public Class<?> supportJavaTypeKey() {
        throw new UnsupportedOperationException("暂不支持,也不需要");
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        throw new UnsupportedOperationException("暂不支持,也不需要");
    }

    @Override
    public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) {
        // 使用字典解析
        String type = getType(contentProperty);
        String label = readCellData.getStringValue();
        //通过type(sex)和label(女)获取value(0)
        String value = DictFrameworkUtils.parseDictDataValue(type, label);
        if (value == null) {
            log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label);
            return null;
        }
        // 将 String 的 value 转换成对应的属性
        Class<?> fieldClazz = contentProperty.getField().getType();
        return Convert.convert(fieldClazz, value);
    }

    @Override
    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty,
                                                    GlobalConfiguration globalConfiguration) {
        // 空时,返回空
        if (object == null) {
            return new WriteCellData<>("");
        }

        // 使用字典格式化
        String type = getType(contentProperty);
        String value = String.valueOf(object);
        //通过type(sex)和value(0)获取label(女)
        String label = DictFrameworkUtils.getDictDataLabel(type, value);
        if (label == null) {
            log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value);
            return new WriteCellData<>("");
        }
        // 生成 Excel 小表格
        return new WriteCellData<>(label);
    }

    /**
     * 通过注解获取
     *
     */
    private static String getType(ExcelContentProperty contentProperty) {
        return contentProperty.getField().getAnnotation(DictFormat.class).value();
    }

}

2. 注解定义

java 复制代码
/**
 * 字典格式化
 * <p>
 * 实现将字典数据的值,格式化成字典数据的标签
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DictFormat {
    /**
     * 例如说,SysDictTypeConstants、InfDictTypeConstants
     *
     * @return 字典类型
     */
    String value();
}

四、设计亮点总结

设计维度 实现方案 解决的问题
声明式配置 通过组合注解实现配置 解耦业务代码与字典逻辑
类型安全转换 使用Hutool的Convert工具 自动处理String→Integer等类型转换
统一异常处理 捕获异常并记录错误日志 避免Excel解析中断
可扩展性 支持任意字典类型配置 方便新增字典类型

五、最佳实践建议

1. 常量统一管理

建议创建字典类型常量类:

java 复制代码
public class DictTypeConstants {
    public static final String CONFIG_TYPE = "infra_config_type";
    public static final String BOOLEAN_STRING = "sys_boolean_string";
}

2. 单元测试方案

java 复制代码
public class DictConvertTest {
    
    @Test
    void testExport() {
        ConfigRespVO vo = new ConfigRespVO();
        vo.setType(1);
        
        // 导出验证Excel内容包含"文本类型"
        EasyExcel.write("test.xlsx", ConfigRespVO.class).sheet().doWrite(Collections.singletonList(vo));
    }

    @Test
    void testImport() {
        // 准备包含"文本类型"的Excel文件
        List<ConfigRespVO> list = EasyExcel.read("test.xlsx")
                .head(ConfigRespVO.class).sheet().doReadSync();
        
        assertEquals(1, list.get(0).getType()); // 验证转换结果
    }
}
相关推荐
学长毕设2 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
驰骋工作流2 小时前
流程时效考核的表结构:从待办时限到 WF_CH 考核结果驰骋BPM驰骋低代码低代码工作流引擎
android·低代码·rxjava
zzu123zsw3 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号4 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
传奇开心果编程4 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
论文复现现场4 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠4 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David5 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放5 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友