java切面-状态字段翻译

一、应用场景

我们在开发过程中可能会遇到以下情况

  • 数据库存储的状态属性是code,比如设备状态存储的是0,1,对用户来讲看到0和1是没有意义的,前端应该展示其中文含义(如:设备状态: 未启动/启动)

在这种情况下,后端需要将code翻译成中文,前端直接展示即可。
小编自己实现了字段翻译的功能,介绍如下

二、代码逻辑

服务启动以后加载字典配置到缓存,缓存会被TranslateAspect切面使用,将原始值翻译成字典配置的中文。

三、具体用法

以下图为例,deviceStatus(被翻译的字段)是数据库的原始值
deviceStatusValue是翻译后的中文字段,@TranslateField注解要加在deviceStatusValue上面;
其中主要是2个配置

  • typeCode = “DICT_DEVICE_STATUS” 字典类的code
  • itemField ="deviceStatus" 以哪个字段的值作为字典项的code进行翻译

java切面-状态字段翻译_第1张图片

@TranslateService用在需要翻译的service上面,切面会拦截这个service出去的消息体,对消息体中添加@TranslateField注解的字段进行翻译,比如把上述设备运行状况的1翻译成自动运行,这个显示值可以在数据库配置,动态调整。
java切面-状态字段翻译_第2张图片

数据库配置如下
java切面-状态字段翻译_第3张图片


1.自定义注解(加在service上)


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TranslateService {

}

2.自定义注解(加在字段上)

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TranslateField {

    String typeCode();

    String itemField();

}

3.自动翻译切面


@Aspect
@Component
public class TranslateAspect {

    private SwitchConfig switchConfig;
    private DictLoadCache dictLoadCache;

    public TranslateAspect(DictLoadCache dictLoadCache, SwitchConfig switchConfig) {
        this.dictLoadCache = dictLoadCache;
        this.switchConfig = switchConfig;
    }

    @Pointcut("@within(com.risen.helper.translate.annotation.TranslateService)")
    private void pointcut() {

    }

    @Around("pointcut()")
    public Object translateAroundInterceptor(ProceedingJoinPoint point) throws Throwable {
        Object result = point.proceed();
        Predicate predicate = s -> s;
        if (predicate.test(switchConfig.getTranslateSwitch())) {
            Optional.ofNullable(result).ifPresent(item -> {
                returnObjectProcessor(item);
            });
        }
        return result;
    }

    private void returnObjectProcessor(Object obj) {
        if (!notEmpty(obj)) {
            return;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        Field[] superFields = new Field[]{};
        Class superClass = obj.getClass().getSuperclass();
        if (notEmpty(superClass)) {
            superFields = superClass.getDeclaredFields();
        }
        Field[] allField = ArrayUtils.addAll(fields, superFields);
        Map fieldMap = Stream.of(allField).collect(Collectors.toMap(s -> s.getName(), s -> s));
        Stream.of(allField).forEach(field -> {
            field.setAccessible(true);
            Class fieldType = field.getType();
            try {
                Object fieldValue = field.get(obj);
                if (List.class.isAssignableFrom(fieldType)) {
                    //如果是list需要递归
                    if (notEmpty(fieldValue)) {
                        List fieldList = (List) fieldValue;
                        fieldList.forEach(item -> {
                            returnObjectProcessor(item);
                        });
                    }
                } else if (Set.class.isAssignableFrom(fieldType)) {
                    //如果是set需要递归
                    if (notEmpty(fieldValue)) {
                        Set fieldSet = (Set) fieldValue;
                        fieldSet.forEach(item -> {
                            returnObjectProcessor(item);
                        });
                    }
                } else if (String.class.isAssignableFrom(fieldType)) {
                    changeReturnValueOfString(field, obj, fieldMap);
                } else if (Object.class.isAssignableFrom(fieldType) && !Number.class.isAssignableFrom(fieldType)) {
                    returnObjectProcessor(field.get(obj));
                }
            } catch (IllegalAccessException e) {
                LogUtil.info("find field :{},error:{}", field.getName(), e.getMessage());
                e.printStackTrace();
            }
        });
    }

    private void changeReturnValueOfString(Field field, Object obj, Map fieldMap) {
        //可能是单个object, object里面又有list,list里面又有object,需要递归处理字段
        TranslateField translateField = field.getDeclaredAnnotation(TranslateField.class);
        if (notEmpty(translateField)) {
            String typeCode = translateField.typeCode();
            String itemField = translateField.itemField();
            Field fieldStr = fieldMap.get(itemField);
            if (notEmpty(typeCode) && notEmpty(itemField) && notEmpty(fieldStr)) {
                fieldStr.setAccessible(true);
                try {
                    Object value = fieldStr.get(obj);
                    if (notEmpty(value)) {
                        field.set(obj, dictLoadCache.getDictNameByCacheKey(typeCode, value));
                    }
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    LogUtil.info("find {} error:{}", fieldStr.getName(), e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }


    private Boolean notEmpty(Object in) {
        Predicate predicate = s -> ObjectUtils.isNotEmpty(s);
        return predicate.test(in);
    }
}

四、字典缓存设计(可自行实现)

字典缓存实现类:服务启动以后加载字典配置到缓存,缓存会被上面的TranslateAspect使用,以此为基础进行翻译。
@Component
public class DictLoadCache extends AgentCacheAbstract> {

    @Autowired
    private DictItemMapper dictItemMapper;

    @Autowired
    private DictTypeMapper dictTypeMapper;


    public DictLoadCache() {
        super(null, null, null);
    }

    @Override
    public void loadCache() {
        LogUtil.info("start load dict cache...");
        List itemList = dictItemMapper.selectList(new LambdaQueryWrapper());
        List typeList = dictTypeMapper.selectList(new LambdaQueryWrapper());
        Optional.ofNullable(typeList).ifPresent(type -> {
            Optional.ofNullable(itemList).ifPresent(item -> {
                Map> itemEntityMap = item.stream().collect(Collectors.groupingBy(DictItemEntity::getTypeCode));
                Set typeSetInfo = type.stream().map(s -> s.getTypeCode()).collect(Collectors.toSet());
                itemEntityMap.forEach((k, v) -> {
                    if (typeSetInfo.contains(k)) {
                        put(k, v.stream().map(s -> {
                            return new DictItemValueDTO(s);
                        }).collect(Collectors.toList()));
                    }
                });
            });
        });
    }

    public String getDictNameByCacheKey(String typeCode, Object value) {
        AtomicReference dictName = new AtomicReference("");
        Predicate predicate = s -> !CollectionUtils.isEmpty(s);
        List ListCache = get(typeCode);
        if (predicate.test(ListCache)) {
            ListCache.forEach(item -> {
                if (item.getItemCode().equals(String.valueOf(value))) {
                    dictName.set(item.getItemName());
                    return;
                }
            });
        }
        return dictName.get();
    }


}
字典缓存抽象类AgentCacheAbstract
@Component
public abstract class AgentCacheAbstract {

    private Integer expire = 4;
    private Integer maximumSize = 100000;
    private Integer initialCapacity = 1024;


    private Cache cache;

    public abstract void loadCache();

    public AgentCacheAbstract(Integer expire, Integer maximumSize, Integer initialCapacity) {
        Optional.ofNullable(expire).ifPresent(s -> {
            this.expire = s;
        });
        Optional.ofNullable(maximumSize).ifPresent(s -> {
            this.maximumSize = s;
        });
        Optional.ofNullable(initialCapacity).ifPresent(s -> {
            this.initialCapacity = s;
        });
        cache = Optional.ofNullable(cache).orElse(Caffeine.newBuilder()
                .expireAfterWrite(this.expire, TimeUnit.HOURS)
                .initialCapacity(this.initialCapacity)
                .maximumSize(this.maximumSize)
                .build());
    }

    public V get(K key) {
        Predicate predicate = s -> ObjectUtils.isEmpty(s);
        V t = cache.getIfPresent(key);
        if (predicate.test(t)) {
            loadCache();
        }
        return cache.getIfPresent(key);
    }

    public void put(K key, V obj) {
        cache.put(key, obj);
    }


    public Boolean containKey(K key) {
        Predicate predicate = s -> ObjectUtils.isEmpty(s);
        V t = cache.getIfPresent(key);
        if (predicate.test(t)) {
            return false;
        }
        return true;
    }


}

五、字典表设计

字典项表设计如下
java切面-状态字段翻译_第4张图片
字典类表设计如下

java切面-状态字段翻译_第5张图片

你可能感兴趣的:(java)