我们在开发过程中可能会遇到以下情况
- 数据库存储的状态属性是code,比如设备状态存储的是0,1,对用户来讲看到0和1是没有意义的,前端应该展示其中文含义(如:设备状态: 未启动/启动)
在这种情况下,后端需要将code翻译成中文,前端直接展示即可。
小编自己实现了字段翻译的功能,介绍如下
服务启动以后加载字典配置到缓存,缓存会被TranslateAspect切面使用,将原始值翻译成字典配置的中文。
三、具体用法以下图为例,deviceStatus(被翻译的字段)是数据库的原始值
deviceStatusValue是翻译后的中文字段,@TranslateField注解要加在deviceStatusValue上面;
其中主要是2个配置
- typeCode = “DICT_DEVICE_STATUS” 字典类的code
- itemField =“deviceStatus” 以哪个字段的值作为字典项的code进行翻译
@TranslateService用在需要翻译的service上面,切面会拦截这个service出去的消息体,对消息体中添加@TranslateField注解的字段进行翻译,比如把上述设备运行状况的1翻译成自动运行,这个显示值可以在数据库配置,动态调整。
数据库配置如下
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
四、字典缓存设计(可自行实现)
字典缓存实现类:服务启动以后加载字典配置到缓存,缓存会被上面的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; } }
五、字典表设计
字典项表设计如下
字典类表设计如下



