SpringBoot 自定义字典翻译组件
本来想在项目里直接用Easy-Trans来做字典翻译,用的时候发现,这个组件不支持原生的mybatis.
于是直接自己写了一个。废话不多说,直接上代码。就两个文件。
import java.lang.annotation.*;/*** 字典翻译注解,用于实体类字段自动翻译*/
@Target(ElementType.FIELD) // 作用于字段
@Retention(RetentionPolicy.RUNTIME) // 运行时生效
@Documented
public @interface DictTrans {/** 字典key(对应Redis中的字典类型) */String dictKey();/** 目标翻译字段名(存储翻译结果的字段) */String targetField();
}
我这里是直接从redis中取出来进行字典进行翻译
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.geek.common.constant.CacheConstants;
import com.geek.common.core.redis.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;/*** 字典翻译切面,自动处理@DictTrans注解的字段翻译*/
@Aspect
@Component
@Slf4j
public class DictTransAspect {@Resourceprivate RedisCache redisCache;/*** 切入点:拦截Service层所有返回实体/集合的方法* (根据实际工程调整包路径,确保覆盖业务Service)*/@AfterReturning(pointcut = "execution(* com.geek.system.service..*(..))", // 拦截system.service包下所有方法returning = "result" // 方法返回值)public void doDictTrans(Object result) {if (result == null) return;// 处理集合类型(List/Set等)if (result instanceof Collection<?>) {Collection<?> collection = (Collection<?>) result;collection.forEach(this::processEntity);} else {// 处理单个实体processEntity(result);}}/*** 处理单个实体对象的字典翻译*/private void processEntity(Object entity) {if (entity == null) return;Class<?> entityClass = entity.getClass();// 获取实体类所有字段(含父类私有字段)List<Field> allFields = getAllFields(entityClass);for (Field field : allFields) {// 检查字段是否标注@DictTrans注解DictTrans dictTrans = field.getAnnotation(DictTrans.class);if (dictTrans == null) continue;try {// 1. 获取源字段(编码字段)的值field.setAccessible(true); // 允许访问私有字段String sourceValue = (String) field.get(entity);if (sourceValue == null) continue; // 编码值为null则跳过// 2. 从Redis获取JSON数组字符串并解析为映射(修正类型不匹配问题)String dictKey = dictTrans.dictKey();String redisFullKey = CacheConstants.SYS_DICT_KEY + dictKey;// 直接获取JSONArray对象(无需字符串解析)JSONArray dictJsonArray = (JSONArray) redisCache.getCacheObject(redisFullKey);if (dictJsonArray == null || dictJsonArray.isEmpty()) {log.error("字典翻译失败:Redis中未找到键[{}]或JSON数组为空", redisFullKey);continue;}// 步骤3:构建dictValue -> dictLabel的映射Map<String, String> dictMap = new HashMap<>();for (int i = 0; i < dictJsonArray.size(); i++) {JSONObject dictItem = dictJsonArray.getJSONObject(i);String dictValue = dictItem.getString("dictValue");String dictLabel = dictItem.getString("dictLabel");dictMap.put(dictValue, dictLabel);}// 获取翻译后的值(补充缺失步骤)String translatedValue = dictMap.get(sourceValue);if (translatedValue == null) {log.warn("字典翻译警告:字典key[{}]中无编码[{}]的映射", dictKey, sourceValue);}// 4. 反射设置目标字段(翻译结果字段)的值String targetFieldName = dictTrans.targetField();Field targetField = entityClass.getDeclaredField(targetFieldName);targetField.setAccessible(true);targetField.set(entity, translatedValue);} catch (NoSuchFieldException e) {log.error("字典翻译反射异常:目标字段[{}]不存在", dictTrans.targetField(), e);} catch (IllegalAccessException e) {log.error("字典翻译反射异常:字段访问权限不足", e);}}}/*** 获取类的所有字段(含父类私有字段)*/private List<Field> getAllFields(Class<?> clazz) {List<Field> fields = new ArrayList<>();while (clazz != null && clazz != Object.class) { // 遍历至Object类停止fields.addAll(Arrays.asList(clazz.getDeclaredFields()));clazz = clazz.getSuperclass(); // 处理父类字段}return fields;}
}
使用方式,直接在实体字段上用注解 @DictTrans
dictKey是对应的字典的key
targetField是翻译后映射的字段
@DictTrans(dictKey = "transaction_type", targetField = "transactionTypeName")private String transactionType ;private String transactionTypeName;