自定义注解实现数据转换(单条和批量),支持aop自动转换和手动转换

以下数据转换框架适用场景:

  • 中大型项目,需要统一转换逻辑
  • 转换规则复杂且多变
  • 对开发效率要求高于极致性能

存在以下优点:

  • 批量优化:通过BatchConverter接口支持批量查询,避免N+1问题
  • 扩展性好:新的转换类型只需实现Converter接口并注册
  • 注解驱动:使用方便,代码侵入性低
  • AOP自动处理:对业务代码透明,支持多种返回类型

存在可替代方案:MapStruct。相比于MapStruct,当前方案灵活性更高,性能方面弱于MapStruct。

一、注解定义

1、数据转换注解

/**
 * 数据转换注解
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataConvert {
    /**
     * 转换类型
     */
    ConvertType type();

    /**
     * 转换值
     * 字典转换 -> 字典编码
     * 常量转换 -> 常量值,格式:"{key1:value1,key2:value2,...}",如:"{1:是,0:否}"
     * 枚举转换 -> 枚举类路径
     */
    String value() default "";

    /**
     * 转换目标字段名
     * 默认:原字段名 + "Str"
     * 如果转换对象不存在该名的字段属性,不会报错
     */
    String targetField() default "";

    /**
     * 是否自动转换
     * true:AOP自动转换
     * false:需要手动调用转换
     */
    boolean autoConvert() default true;

    /**
     * 转换分组
     * 用于手动触发时指定分组
     */
    String group() default "default";

    /**
     * 批量转换缓存key
     * 相同key的转换会合并为一次批量查询
     * 如:a对象中存在 field1、field2,b对象中存在 field3,这三个字段都是部门转换,设置为相同的key则会合并为一次批量查询在转换
     */
    String batchKey() default "";
}

2、转换类型枚举

/**
 * 转换类型枚举
 */
public enum ConvertType {
    /**
     * 字典转换
     */
    DICT,
    /**
     * 枚举转换
     */
    ENUM,
    /**
     * 常量转换
     */
    CONSTANT,
    /**
     * 部门
     */
    DEPARTMENT,
    /**
     * 用户
     */
    USER,
    /**
     * 角色
     */
    ROLE,
    /**
     * 自定义转换
     */
    CUSTOM
}

二、转换上下文和任务模型

1、批量转换上下文

/**
 * 批量转换上下文
 */
@Data
public class BatchConvertContext {
    /**
     * 所有需要转换的源对象
     */
    private List<Object> sources = new ArrayList<>();

    /**
     * 字段缓存
     */
    private final FieldCache fieldCache;

    /**
     * 转换任务映射
     * 结构:转换类型 -> (批量key -> 转换任务)
     */
    private Map<ConvertType, Map<String, BatchConvertTask>> tasks = new ConcurrentHashMap<>();

    public BatchConvertContext(FieldCache fieldCache) {
        this.fieldCache = fieldCache;
    }

    /**
     * 添加转换任务到上下文
     *
     * @param source      源对象
     * @param sourceField 源字段
     * @param annotation  转换注解
     */
    public void addTask(Object source, Field sourceField, DataConvert annotation) {
        sources.add(source);

        ConvertType type = annotation.type();
        String batchKey = getBatchKey(annotation, sourceField);

        // 按类型和批量key分组任务
        tasks.computeIfAbsent(type, k -> new ConcurrentHashMap<>())
                .computeIfAbsent(batchKey, k -> new BatchConvertTask(type, annotation.value(), batchKey, fieldCache))
                .addConvertItem(source, sourceField, annotation);
    }

    /**
     * 生成批量转换的缓存key
     */
    private String getBatchKey(DataConvert annotation, Field sourceField) {
        if (!annotation.batchKey().isEmpty()) {
            return annotation.batchKey();
        }
        return annotation.type() + ":" + annotation.value();
    }

}

2、批量转换任务

/**
 * 批量转换任务
 */
@Data
public class BatchConvertTask {
    /**
     * 转换类型
     */
    private ConvertType type;

    /**
     * 转换参数值
     */
    private String convertValue;

    /**
     * 批量key
     */
    private String batchKey;

    /**
     * 字段缓存
     */
    private final FieldCache fieldCache;

    /**
     * 转换项列表
     */
    private List<ConvertItem> items = new ArrayList<>();

    public BatchConvertTask(ConvertType type, String convertValue, String batchKey, FieldCache fieldCache) {
        this.type = type;
        this.convertValue = convertValue;
        this.batchKey = batchKey;
        this.fieldCache = fieldCache;
    }

    /**
     * 添加转换项
     */
    public void addConvertItem(Object source, Field sourceField, DataConvert annotation) {
        items.add(new ConvertItem(source, sourceField, annotation));
    }

    /**
     * 获取所有需要转换的源值
     */
    public Set<Object> getSourceValues() {
        return items.stream()
                .map(item -> {
                    try {
                        Field sourceField = item.getSourceField();
                        return sourceField.get(item.getSource());
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("数据转换: 获取字段值失败: " + item.getSourceField().getName(), e);
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
    }

}

3、单个转换项

/**
 * 单个转换项
 */
@Data
@AllArgsConstructor
public class ConvertItem {
    /**
     * 源对象
     */
    private Object source;

    /**
     * 源字段
     */
    private Field sourceField;

    /**
     * 转换注解配置
     */
    private DataConvert annotation;

}

4、字段缓存

/**
 * 字段缓存
 * 用于缓存单次转换过程中的字段信息,提升反射性能
 */
public class FieldCache {

    /**
     * 类字段缓存
     * 结构:Class -> (字段名 -> Field)
     */
    private final Map<Class<?>, Map<String, Field>> classFieldCache = new ConcurrentHashMap<>();

    /**
     * 获取类的字段
     */
    public Field getField(Class<?> clazz, String fieldName) {
        return classFieldCache
                .computeIfAbsent(clazz, k -> new ConcurrentHashMap<>())
                .computeIfAbsent(fieldName, k -> {
                    try {
                        Field field = clazz.getDeclaredField(fieldName);
                        field.setAccessible(true);
                        return field;
                    } catch (NoSuchFieldException e) {
                        return null;
                    }
                });
    }

    /**
     * 获取类的所有字段
     */
    public Field[] getDeclaredFields(Class<?> clazz) {
        Map<String, Field> fieldMap = classFieldCache.get(clazz);
        if (fieldMap != null) {
            return fieldMap.values().toArray(new Field[0]);
        }

        // 首次访问,扫描并缓存所有字段
        Field[] fields = clazz.getDeclaredFields();
        Map<String, Field> newFieldMap = new ConcurrentHashMap<>();
        for (Field field : fields) {
            field.setAccessible(true);
            newFieldMap.put(field.getName(), field);
        }
        classFieldCache.put(clazz, newFieldMap);
        return fields;
    }

    /**
     * 清空缓存
     */
    public void clear() {
        classFieldCache.clear();
    }
}

三、转换器接口和基础实现

1、转换器接口

/**
 * 转换器接口
 * 所有转换器都需要实现此接口
 */
public interface Converter {

    /**
     * 判断是否支持该转换类型
     */
    boolean supports(ConvertType type);

    /**
     * 单条数据转换
     *
     * @param source       源对象
     * @param sourceField  源字段
     * @param targetField  目标字段名
     * @param convertValue 转换参数值
     */
    void convert(Object source, Field sourceField, String targetField, String convertValue);

}

2、批量转换器接口

/**
 * 批量转换器接口
 * 支持批量优化的转换器需要实现此接口
 */
public interface BatchConverter extends Converter {

    /**
     * 批量转换
     *
     * @param task 批量转换任务
     */
    void batchConvert(BatchConvertTask task);

    /**
     * 是否支持批量转换
     *
     * @return true-支持,false-不支持
     */
    default boolean supportBatch() {
        return true;
    }

}

四、具体转换器实现(示例)

1、字典转换器

/**
 * 字典转换器
 */
@Slf4j
@Component
public class DictConverter implements BatchConverter {

	//字典服务
    @Resource
    private DictServer dictServer;

    @Override
    public boolean supports(ConvertType type) {
        return ConvertType.DICT == type;
    }

    @Override
    public void convert(Object source, Field sourceField, String targetField, String dictType) {
        // 单条转换实现
        try {
            Object sourceValue = sourceField.get(source);
            if (sourceValue != null) {
                SysDictionaryItem dictItem = dictServer.byTypeValue(dictType, sourceValue.toString());
                ConverterUtil.setTargetFieldValue(source, targetField, dictItem != null ? dictItem.getDiName() : "未知");
            }
        } catch (Exception e) {
            log.error("数据转换: 字典.单条转换失败, 字段: {}, 字典类型: {}", sourceField.getName(), dictType, e);
            throw new MyInmException("数据转换: 字典.转换失败");
        }
    }

    @Override
    public void batchConvert(BatchConvertTask task) {
        String dictType = task.getConvertValue();

        Map<String, SysDictionaryItem> dictMap = MapUtil.listToMap(
                dictServer.listByCodes(Collections.singletonList(dictType)),
                SysDictionaryItem::getDiValue
        );

        FieldCache fieldCache = task.getFieldCache();
        // 批量设置结果
        for (ConvertItem item : task.getItems()) {
            try {
                Field sourceField = item.getSourceField();
                Object sourceValue = sourceField.get(item.getSource());
                if (sourceValue != null) {
                    String targetField = ConverterUtil.getTargetField(sourceField, item.getAnnotation());
                    SysDictionaryItem dictItem = dictMap.get(sourceValue.toString());
                    ConverterUtil.setTargetFieldValue(item.getSource(), targetField,
                            dictItem != null ? dictItem.getDiName() : "未知",
                            fieldCache);
                }
            } catch (Exception e) {
                log.warn("数据转换: 字典.转换失败: {}", e.getMessage());
            }
        }
    }

}

2、枚举转换器

/**
 * 枚举转换器
 */
@Slf4j
@Component
public class EnumConverter implements BatchConverter {

    // 枚举映射关系
    private final Map<String, Map<String, String>> enumMappingCache = new ConcurrentHashMap<>();

    // 优先查找的字段名
    // 根据 VALUE_FIELD_NAMES 枚举值匹配,转换成 DISPLAY_FIELD_NAMES 枚举值
    // 如果枚举类中不存在以下字段则不作转换
    private static final String[] VALUE_FIELD_NAMES = {"value", "code"};
    private static final String[] DISPLAY_FIELD_NAMES = {"name", "label"};

    @Override
    public boolean supports(ConvertType type) {
        return ConvertType.ENUM == type;
    }

    @Override
    public void convert(Object source, Field sourceField, String targetField, String enumClassPath) {
        try {
            Object sourceValue = sourceField.get(source);
            if (sourceValue == null) {
                return;
            }
            Map<String, String> mapping = getCachedEnumMapping(enumClassPath);
            String displayName = mapping.get(sourceValue.toString());

            ConverterUtil.setTargetFieldValue(source, targetField,
                    displayName != null ? displayName : "未知");
        } catch (IllegalAccessException e) {
            log.error("数据转换: 枚举.单条转换失败, 字段: {}, 枚举类: {}", sourceField.getName(), enumClassPath, e);
            throw new MyInmException("数据转换: 枚举.转换失败");
        }
    }

    @Override
    public void batchConvert(BatchConvertTask task) {
        String enumClassPath = task.getConvertValue();
        FieldCache fieldCache = task.getFieldCache();

        // 获取缓存的枚举映射
        Map<String, String> enumMapping = getCachedEnumMapping(enumClassPath);

        // 批量设置结果
        for (ConvertItem item : task.getItems()) {
            try {
                Field sourceField = item.getSourceField();
                Object sourceValue = sourceField.get(item.getSource());
                if (sourceValue == null) {
                    return;
                }
                String targetField = ConverterUtil.getTargetField(sourceField, item.getAnnotation());
                String displayName = enumMapping.get(sourceValue.toString());

                ConverterUtil.setTargetFieldValue(item.getSource(), targetField,
                        displayName != null ? displayName : "未知", fieldCache);
            } catch (Exception e) {
                log.warn("数据转换: 枚举.转换失败, 源字段: {}, 错误: {}",
                        item.getSourceField().getName(), e.getMessage());
            }
        }
    }

    /**
     * 获取缓存的枚举映射,如果不存在则构建
     */
    private Map<String, String> getCachedEnumMapping(String enumClassPath) {
        return enumMappingCache.computeIfAbsent(enumClassPath, this::buildEnumMapping);
    }

    /**
     * 构建枚举值到显示名称的映射
     */
    private Map<String, String> buildEnumMapping(String enumClassPath) {
        Map<String, String> mapping = new ConcurrentHashMap<>();

        try {
            Class<?> enumClass = Class.forName(enumClassPath);
            if (!enumClass.isEnum()) {
                log.warn("数据转换: 枚举.类不是枚举类型: {}", enumClassPath);
                return mapping;
            }

            Arrays.stream(enumClass.getEnumConstants())
                    .forEach(enumConstant -> processEnumConstant(enumConstant, mapping));

        } catch (ClassNotFoundException e) {
            log.error("数据转换: 枚举.类未找到: {}", enumClassPath, e);
        } catch (Exception e) {
            log.error("数据转换: 枚举.构建映射失败: {}", enumClassPath, e);
        }

        return mapping;
    }

    /**
     * 处理单个枚举常量
     */
    private void processEnumConstant(Object enumConstant, Map<String, String> mapping) {
        try {
            String enumValue = getEnumFieldValue(enumConstant, VALUE_FIELD_NAMES);
            String displayName = getEnumFieldValue(enumConstant, DISPLAY_FIELD_NAMES);

            if (enumValue != null) {
                mapping.put(enumValue, displayName);
            }

            // 同时将枚举名称作为key映射
            String enumName = ((Enum<?>) enumConstant).name();
            mapping.put(enumName, displayName);

        } catch (Exception e) {
            log.warn("数据转换: 枚举.处理枚举常量失败: {}", enumConstant, e);
        }
    }

    /**
     * 获取枚举指定字段的值
     */
    private String getEnumFieldValue(Object enumConstant, String[] fieldNames) {
        // 优先尝试指定字段
        for (String fieldName : fieldNames) {
            try {
                Field field = enumConstant.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Object value = field.get(enumConstant);
                if (value != null) {
                    return value.toString();
                }
            } catch (NoSuchFieldException e) {
                // 字段不存在,继续尝试下一个

            } catch (Exception e) {
                log.debug("数据转换: 枚举.获取字段[{}]值失败: {}", fieldName, e.getMessage());
            }
        }

        // 所有指定字段都不存在或为null,返回枚举名称
        return ((Enum<?>) enumConstant).name();
    }

}

3、常量转换器

/**
 * 常量转换器
 */
@Slf4j
@Component
public class ConstantConverter implements BatchConverter {

    // 常量映射关系
    private final Map<String, Map<String, String>> constantCache = new ConcurrentHashMap<>();

    @Override
    public boolean supports(ConvertType type) {
        return ConvertType.CONSTANT == type;
    }

    @Override
    public void convert(Object source, Field sourceField, String targetField, String constantMapping) {
        try {
            Object sourceValue = sourceField.get(source);
            if (sourceValue == null) {
                return;
            }

            Map<String, String> mapping = getCachedConstantMapping(constantMapping);
            String displayValue = mapping.get(sourceValue.toString());
            ConverterUtil.setTargetFieldValue(source, targetField,
                    displayValue != null ? displayValue : "未知");

        } catch (IllegalAccessException e) {
            log.error("数据转换: 常量.单条转换失败, 字段: {}, 常量映射: {}", sourceField.getName(), constantMapping, e);
            throw new MyInmException("数据转换: 常量.转换失败");
        }
    }

    @Override
    public void batchConvert(BatchConvertTask task) {
        String constantMapping = task.getConvertValue();
        FieldCache fieldCache = task.getFieldCache();

        // 获取缓存的常量映射
        Map<String, String> mapping = getCachedConstantMapping(constantMapping);

        // 批量设置结果
        for (ConvertItem item : task.getItems()) {
            try {
                Field sourceField = item.getSourceField();
                Object sourceValue = sourceField.get(item.getSource());
                if (sourceValue == null) {
                    return;
                }
                String targetField = ConverterUtil.getTargetField(sourceField, item.getAnnotation());
                String displayValue = mapping.get(sourceValue.toString());

                ConverterUtil.setTargetFieldValue(item.getSource(), targetField,
                        displayValue != null ? displayValue : "未知", fieldCache);
            } catch (Exception e) {
                log.warn("数据转换: 常量.转换失败, 源字段: {}, 错误: {}",
                        item.getSourceField().getName(), e.getMessage());
            }
        }
    }

    /**
     * 获取缓存的常量映射,如果不存在则解析
     */
    private Map<String, String> getCachedConstantMapping(String constantMapping) {
        return constantCache.computeIfAbsent(constantMapping, this::parseConstantMapping);
    }

    /**
     * 解析常量映射字符串
     * 格式:"{key1:value1,key2:value2,...}",如:"{1:是,0:否}"
     */
    private Map<String, String> parseConstantMapping(String constantMapping) {
        if (!StringUtils.hasText(constantMapping)) {
            log.warn("数据转换: 常量.映射字符串为空");
            return Collections.emptyMap();
        }

        // 移除首尾的大括号
        String mappingStr = constantMapping.trim();
        if (mappingStr.startsWith("{") && mappingStr.endsWith("}")) {
            mappingStr = mappingStr.substring(1, mappingStr.length() - 1).trim();
        }

        if (!StringUtils.hasText(mappingStr)) {
            log.warn("数据转换: 常量.映射内容为空");
            return Collections.emptyMap();
        }

        Map<String, String> mapping = new HashMap<>();

        try {
            // 按逗号分割键值对
            String[] keyValuePairs = mappingStr.split(",");
            for (String pair : keyValuePairs) {
                if (!StringUtils.hasText(pair)) {
                    continue;
                }
                // 按冒号分割键值
                String[] keyValue = pair.split(":", 2);
                if (keyValue.length != 2) {
                    log.warn("数据转换: 常量.无效的键值对格式: {}", pair);
                    continue;
                }
                String key = keyValue[0].trim();
                String value = keyValue[1].trim();
                if (StringUtils.hasText(key) && StringUtils.hasText(value)) {
                    mapping.put(key, value);
                }
            }
            log.debug("数据转换: 常量.解析映射成功, 条目数: {}", mapping.size());
        } catch (Exception e) {
            log.error("数据转换: 常量.解析映射失败: {}", constantMapping, e);
        }
        return mapping;
    }

}

五、转换管理器

/**
 * 转换管理器
 */
@Slf4j
@Component

public class ConvertManager {

    @Autowired
    private List<Converter> converters;

    /**
     * 转换器映射缓存
     */
    private final Map<ConvertType, Converter> converterMap = new ConcurrentHashMap<>();

    /**
     * 初始化方法
     * 扫描所有转换器并建立映射关系
     */
    @PostConstruct
    public void init() {
        log.info("数据转换: 初始化转换管理器...");

        for (Converter converter : converters) {
            for (ConvertType type : ConvertType.values()) {
                if (converter.supports(type)) {
                    converterMap.put(type, converter);
                    log.debug("数据转换: 注册转换器: {} -> {}", type, converter.getClass().getSimpleName());
                    break;
                }
            }
        }

        log.info("数据转换: 转换管理器初始化完成,共注册 {} 个转换器", converterMap.size());
    }

    /**
     * 根据转换类型获取对应的转换器
     */
    public Converter getConverter(ConvertType type) {
        return converterMap.get(type);
    }

    /**
     * 检查是否支持该转换类型
     */
    public boolean hasConverter(ConvertType type) {
        return converterMap.containsKey(type);
    }
}

六、转换助手

/**
 * 数据转换助手
 */
@Slf4j
@Component
public class DataConvertHelper {

    @Autowired
    private ConvertManager convertManager;

    /**
     * 转换单个对象
     *
     * @param data 需要转换的数据
     */
    public void convert(Object data) {
        if (data == null) {
            log.debug("数据转换: 转换对象为空");
            return;
        }
        // 单个对象使用列表方式处理
        batchConvertList(Collections.singletonList(data));
    }

    /**
     * 转换对象列表
     * 对列表数据进行批量转换优化
     *
     * @param list 需要转换的对象列表
     */
    public void batchConvertList(List<?> list) {
        if (list == null || list.isEmpty()) {
            log.debug("数据转换: 转换列表为空");
            return;
        }
        log.debug("数据转换: 开始处理 {} 个对象", list.size());
        FieldCache fieldCache = new FieldCache();
        try {
            // 构建转换上下文
            BatchConvertContext context = buildConvertContext(list, fieldCache);
            if (context.getTasks().isEmpty()) {
                log.debug("数据转换: 未发现需要转换的字段");
                return;
            }
            // 执行转换
            executeBatchConvert(context);
            log.debug("数据转换: 完成所有转换任务");
        } finally {
            fieldCache.clear();
        }
    }

    /**
     * 构建转换上下文
     * 扫描所有对象的转换需求并进行分组
     */
    private BatchConvertContext buildConvertContext(List<?> list, FieldCache fieldCache) {
        BatchConvertContext context = new BatchConvertContext(fieldCache);

        for (Object obj : list) {
            if (obj == null) continue;

            // 缓存的字段信息
            Field[] fields = fieldCache.getDeclaredFields(obj.getClass());
            for (Field field : fields) {
                DataConvert annotation = field.getAnnotation(DataConvert.class);
                if (isConvertible(annotation)) {
                    context.addTask(obj, field, annotation);
                    log.trace("数据转换: 添加转换任务: {}.{}", obj.getClass().getSimpleName(), field.getName());
                }
            }
        }

        return context;
    }

    /**
     * 判断字段是否需要转换
     */
    private boolean isConvertible(DataConvert annotation) {
        return annotation != null && annotation.autoConvert();
    }

    /**
     * 执行批量转换
     * 按转换类型分组执行,支持批量优化
     */
    private void executeBatchConvert(BatchConvertContext context) {
        for (Map.Entry<ConvertType, Map<String, BatchConvertTask>> typeEntry : context.getTasks().entrySet()) {
            ConvertType type = typeEntry.getKey();
            Converter converter = convertManager.getConverter(type);

            if (converter == null) {
                log.warn("数据转换: 未找到类型 {} 对应的转换器", type);
                continue;
            }

            // 处理该类型的所有任务
            for (BatchConvertTask task : typeEntry.getValue().values()) {
                processConvertTask(converter, task);
            }
        }
    }

    /**
     * 处理单个转换任务
     * 优先使用批量转换,不支持时降级为单条转换
     */
    private void processConvertTask(Converter converter, BatchConvertTask task) {
        if (converter instanceof BatchConverter && ((BatchConverter) converter).supportBatch()) {
            // 使用批量转换
            log.debug("数据转换: 批量转换处理任务: {}, 项目数: {}", task.getBatchKey(), task.getItems().size());
            ((BatchConverter) converter).batchConvert(task);
        } else {
            // 降级为单条转换
            log.debug("数据转换: 单条转换处理任务: {}, 项目数: {}", task.getBatchKey(), task.getItems().size());
            for (ConvertItem item : task.getItems()) {
                converter.convert(
                        item.getSource(),
                        item.getSourceField(),
                        ConverterUtil.getTargetField(item.getSourceField(), item.getAnnotation()),
                        item.getAnnotation().value()
                );
            }
        }
    }
}

七、AOP切面配置

/**
 * 数据转换切面
 * 自动拦截Controller返回结果并进行数据转换
 */
@Slf4j
@Aspect
@Component
public class DataConvertAspect {

    @Autowired
    private DataConvertHelper dataConvertHelper;

    /**
     * 切入点定义:所有Controller类的方法
     */
    @Pointcut("within(@org.springframework.stereotype.Controller *) || " +
            "within(@org.springframework.web.bind.annotation.RestController *)")
    public void controllerPointcut() {
    }

    /**
     * 环绕通知
     * 在Controller方法执行后对返回结果进行数据转换
     */
    @Around("controllerPointcut()")
    public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        if (result != null) {
            try {
                convertResult(result);
            } catch (Exception e) {
                log.error("数据转换: 数据转换失败", e);
            }
        }
        return result;
    }

    /**
     * 转换返回结果
     * 根据结果类型进行相应的转换处理
     */
    private void convertResult(Object result) {
        if (result instanceof R) {
            // 统一响应结果封装
            convertResponseResult((R<?>) result);
        } else if (result instanceof List) {
            // 列表结果
            dataConvertHelper.batchConvertList((List<?>) result);
        } else if (result instanceof IPage) {
            // 分页结果
            convertPageResult((IPage<?>) result);
        } else {
            // 单个对象结果
            convertSingleObject(result);
        }
    }

    /**
     * 转换统一响应结果
     */
    private void convertResponseResult(R<?> responseResult) {
        Object data = responseResult.getData();
        if (data != null) {
            convertResponseData(data);
        }
    }

    /**
     * 转换响应数据
     */
    private void convertResponseData(Object data) {
        if (data instanceof List) {
            dataConvertHelper.batchConvertList((List<?>) data);
        } else if (data instanceof IPage) {
            convertPageResult((IPage<?>) data);
        } else {
            convertSingleObject(data);
        }
    }

    /**
     * 转换分页结果
     */
    private void convertPageResult(IPage<?> page) {
        if (page != null && page.getRecords() != null) {
            dataConvertHelper.batchConvertList(page.getRecords());
        }
    }

    /**
     * 转换单个对象
     */
    private void convertSingleObject(Object obj) {
        dataConvertHelper.convert(obj);
    }
}

八、工具类

/**
 * 数据转换工具类,供具体转换器使用
 */
 @Slf4j
public class ConverterUtil {

    /**
     * 设置目标字段值
     */
    public static void setTargetFieldValue(Object obj, String fieldName, Object value, FieldCache fieldCache) {
        try {
            Field targetField = fieldCache.getField(obj.getClass(), fieldName);
            if (targetField != null) {
                targetField.set(obj, value);
            } else {
                // 目标字段不存在时忽略,可能是可选字段
                log.debug("目标字段不存在: {}.{}", obj.getClass().getSimpleName(), fieldName);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException("设置字段值失败: " + fieldName, e);
        }
    }

    /**
     * 设置目标字段值
     */
    public static void setTargetFieldValue(Object obj, String fieldName, Object value) {
        try {
            Field targetField = obj.getClass().getDeclaredField(fieldName);
            targetField.setAccessible(true);
            targetField.set(obj, value);
        } catch (NoSuchFieldException e) {
            // 目标字段不存在时忽略
        } catch (IllegalAccessException e) {
            throw new RuntimeException("设置字段值失败: " + fieldName, e);
        }
    }

    /**
     * 获取目标字段名
     * 根据注解配置生成目标字段名
     */
    public static String getTargetField(Field sourceField, DataConvert annotation) {
        if (!annotation.targetField().isEmpty()) {
            return annotation.targetField();
        }
        return sourceField.getName() + "Str";
    }

}

/**
 * map工具类
 */
public class MapUtil {

    /**
     * List 转成 Map
     *
     * @param list      list数据
     * @param keyMapper 取主键的 Lambda
     * @param <T>       VO 类型
     * @param <K>       主键类型
     * @return Map<主键, VO>
     */
    public static <T, K> Map<K, T> listToMap(List<T> list, Function<T, K> keyMapper) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(
                keyMapper,
                Function.identity(),
                (oldVal, newVal) -> oldVal));
    }

}

九、使用说明及示例

部分代码需要根据实际进行调整,如返回包装类R、字典服务、字典转换器实现等。
注意:aop需启用aop支持

实体使用示例

/**
 * 用户视图对象
 * 演示数据转换注解的使用
 */
@Data
public class UserVO {
    
    private Long id;
    private String username;
    
    /**
     * 用户状态 - 字典转换
     * 将状态码转换为状态名称
     */
    @DataConvert(type = ConvertType.DICT, value = "user_status")
    private Integer status;
    private String statusStr; // 自动转换的目标字段
    
    /**
     * 性别 - 枚举转换  
     * 将性别编码转换为性别名称
     * 这里可以在注解中加一个字段,用于保存枚举类,不使用类路径的方式
     */
    @DataConvert(type = ConvertType.ENUM, value = "com.example.enums.GenderEnum")
    private Integer gender;
    private String genderStr;
    
    /**
     * 部门ID - 部门转换
     * 将部门ID转换为部门名称
     */
    @DataConvert(type = ConvertType.DEPARTMENT, targetField = "deptName")
    private Long deptId;
    private String deptName;
    
    /**
     * 创建人 - 用户转换(手动触发)
     * 需要手动调用转换,不自动转换
     */
    @DataConvert(type = ConvertType.USER, targetField = "creatorName", 
                autoConvert = false, group = "detail")
    private Long creatorId;
    private String creatorName;
    
    /**
     * 角色ID - 角色转换
     * 将角色ID转换为角色名称
     */
    @DataConvert(type = ConvertType.ROLE, targetField = "roleName")
    private Long roleId;
    private String roleName;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值