Skip to content

字典注解

定义注解

java

package org.elsfs.cloud.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 字典注解
 *
 * @author zeng
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
  /**
   * 用于区分不同业务
   *
   * @return 用于区分不同业务
   */
  String dictType();

  /**
   * 目标显示属性(待绑定属性,注意非数据库字段请排除)
   *
   * @return 目标显示属性(待绑定属性,注意非数据库字段请排除)
   */
  String target();

  /**
   * 数据字典表
   *
   * @return 数据字典表
   */
  String dictTable() default "";
}
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 返回值 字典翻译
 *
 * @author zeng
 */
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictionaryTranslation {}

定义拦截器

java

package org.elsfs.cloud.dict.aop;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.elsfs.cloud.core.annotation.Dict;
import org.elsfs.cloud.core.aop.AnnotationAdvisor;
import org.elsfs.cloud.core.utils.StringUtil;
import org.elsfs.cloud.core.vo.R;
import org.elsfs.cloud.dict.service.DictService;

/**
 * 执行翻译
 *
 * @see org.springframework.aop.Advisor
 * @see AnnotationAdvisor
 * @author zeng
 */
@Slf4j
@RequiredArgsConstructor
public class DictMethodInterceptor implements MethodInterceptor {
  /** DictService的保护级别的最终成员变量。 这是一个字典服务,用于在类中进行字典相关的操作。 */
  protected final DictService dictService;

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    long start = System.currentTimeMillis();
    Object returnVar = invocation.proceed();
    if (ignoreType(returnVar)) {
      return returnVar;
    }
    LOGGER.debug("运行时间:" + (System.currentTimeMillis() - start) + "毫秒");
    parseResult(returnVar);
    return returnVar;
  }

  /**
   * 翻译返回结果
   *
   * @param result result
   */
  public void parseResult(Object result) {
    // 判断结果类型
    if (result instanceof List<?> resultList) {
      resultList.forEach(this::parseDict);
    }
    if (result instanceof R<?> r) {
      parseResult(r.getResult());
    }
    if (result instanceof IPage<?> page) {
      parseResult(page.getRecords());
    }
    parseDict(result);
  }

  /**
   * 解析带有Dict注解的对象字典。 该方法通过反射,获取对象类中所有带有Dict注解的字段,并对这些字段进行处理。
   *
   * @param obj 需要解析的对象。
   */
  private void parseDict(Object obj) {
    // 使用反射缓存获取对象类的所有带有Dict注解的字段
    var annotatedFields = ReflectionCache.getAnnotatedFields(obj.getClass());
    annotatedFields.forEach(field -> handleDict(obj, field));
  }

  /**
   * 处理字典映射,将一个字段的值映射到另一个字段上。
   *
   * @param obj 要处理的对象实例。
   * @param field 需要被映射的字段。
   */
  private void handleDict(Object obj, Field field) {
    try {
      var dict = field.getAnnotation(Dict.class);
      var target = dict.target(); // 目标字段名
      var targetField = ReflectionCache.getCachedDeclaredField(obj.getClass(), target);
      if (targetField == null) {
        return;
      }
      // 允许当前字段和目标字段可访问
      field.setAccessible(true);
      var key = field.get(obj);
      if (ObjectUtil.isEmpty(key)) {
        return;
      }
      if (StringUtil.isBlank(key.toString())) {
        return;
      }
      targetField.setAccessible(true);
      // 根据字典类型和当前字段的值,更新目标字段的值
      var dictItemEntities = dictService.getListByDictType(dict.dictType());
      for (var item : dictItemEntities) {
        if (Objects.equals(item.getLabel(), key.toString())) {
          targetField.set(obj, item.getValue());
        }
      }
      // 重置字段访问权限
      targetField.setAccessible(false);
      field.setAccessible(false);
    } catch (IllegalAccessException e) {
      LOGGER.error("Failed to handle dictionary field: " + field.getName(), e);
    }
  }

  private boolean ignoreType(Object returnVar) {
    return returnVar == null || returnVar instanceof CharSequence || returnVar instanceof Number;
  }
}

注入 bean

java
  /**
   * 创建并返回一个注释类型的Advisor Bean,它基于DictionaryTranslation注解提供拦截器功能。
   * 这个方法的作用是启用对标注了DictionaryTranslation注解的方法的拦截,具体的拦截逻辑由传入的DictMethodInterceptor实现。
   *
   * @param dictMethodInterceptor 拦截器实例,负责处理注解方法的拦截逻辑。
   * @return 返回一个配置好的Advisor实例,它包含了注解拦截器的配置信息。
   */
  @Bean
  @Role(BeanDefinition.ROLE_SUPPORT)
  @ConditionalOnBean(DictMethodInterceptor.class)
  public Advisor dictionaryAnnotationAdvisor(DictMethodInterceptor dictMethodInterceptor) {
    // 创建AnnotationAdvisor,将拦截器和注解绑定
    return new AnnotationAdvisor(dictMethodInterceptor, DictionaryTranslation.class);
  }

使用

java

package org.elsfs.cloud.dict.entity;

import lombok.Data;
import org.elsfs.cloud.core.annotation.Dict;

/**
 * 测试翻译实体
 *
 * @author zeng
 */
@Data
public class DictionaryTranslationEntityTest {
  /** 字典翻译原始值 性别 */
  @Dict(target = "sexText", dictType = "sexType")
  Integer sex;

  /** 翻译性别值 */
  String sexText;

  @Dict(target = "statusText", dictType = "status")
  private String status;

  private String statusText;
}


package org.elsfs.cloud.dict.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.ArrayList;
import java.util.List;
import org.elsfs.cloud.core.annotation.DictionaryTranslation;
import org.elsfs.cloud.core.vo.R;
import org.elsfs.cloud.dict.entity.DictionaryTranslationEntityTest;
import org.springframework.stereotype.Service;

/**
 * 测试的服务
 *
 * @author zeng
 */
@Service
public class DictionaryTranslationEntityServiceTest {
  private static final List<DictionaryTranslationEntityTest> LIST = new ArrayList<>();

  static {
    var entityText = new DictionaryTranslationEntityTest();
    entityText.setSex(1);
    entityText.setStatus("ENABLE");
    LIST.add(entityText);
    var entityText2 = new DictionaryTranslationEntityTest();
    entityText2.setSex(2);
    entityText2.setStatus("DISABLE");
    LIST.add(entityText2);
  }

  /**
   * list
   *
   * @return list
   */
  @DictionaryTranslation
  public List<DictionaryTranslationEntityTest> list() {
    return LIST;
  }

  /**
   * entity
   *
   * @return entity
   */
  @DictionaryTranslation
  public DictionaryTranslationEntityTest entity() {
    return LIST.get(0);
  }

  /**
   * returnNull
   *
   * @return returnNull
   */
  @DictionaryTranslation
  public DictionaryTranslationEntityTest returnNull() {
    return null;
  }

  /**
   * entityNull
   *
   * @return entityNull
   */
  @DictionaryTranslation
  public DictionaryTranslationEntityTest entityNull() {
    return new DictionaryTranslationEntityTest();
  }

  /**
   * page
   *
   * @return page
   */
  @DictionaryTranslation
  public IPage<DictionaryTranslationEntityTest> page() {
    Page<DictionaryTranslationEntityTest> page = new Page<>();
    page.setRecords(LIST);
    return page;
  }

  /**
   * resR
   *
   * @return resR
   */
  @DictionaryTranslation
  public R<IPage<DictionaryTranslationEntityTest>> resR() {
    Page<DictionaryTranslationEntityTest> page = new Page<>();
    page.setRecords(LIST);
    return R.success(page);
  }
}


package org.elsfs.cloud.dict;

import org.elsfs.cloud.core.annotation.DictionaryTranslation;
import org.elsfs.cloud.core.aop.AnnotationAdvisor;
import org.elsfs.cloud.dict.service.impl.DictionaryTranslationEntityServiceTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;

/**
 * 测试{@link DictionaryTranslation} 和{@link AnnotationAdvisor}搭配使用
 *
 * @author zeng
 */
@SpringBootTest
@ComponentScan("org.elsfs.cloud")
public class DictionaryTranslationTest {

  @Autowired DictionaryTranslationEntityServiceTest serviceTest;

  @Test
  void list() {
    var list = serviceTest.list();
    Assertions.assertNotNull(list);
    Assertions.assertDoesNotThrow(
        () ->
            list.forEach(
                item -> {
                  Assertions.assertNotNull(item.getSexText());
                  Assertions.assertNotNull(item.getStatusText());
                }));
  }

  @Test
  void entity() {
    var entity = serviceTest.entity();
    Assertions.assertNotNull(entity);
    Assertions.assertNotNull(entity.getSexText());
    Assertions.assertNotNull(entity.getStatusText());
  }

  @Test
  void returnNull() {
    var entity = serviceTest.returnNull();
    Assertions.assertNull(entity);
  }

  @Test
  void entityNull() {
    var entity = serviceTest.entityNull();
    Assertions.assertNotNull(entity);
    Assertions.assertNull(entity.getSexText());
    Assertions.assertNull(entity.getStatusText());
  }

  @Test
  void page() {
    var page = serviceTest.page();
    System.out.println(page.getRecords());
    Assertions.assertNotNull(page);
    Assertions.assertDoesNotThrow(
        () ->
            page.getRecords()
                .forEach(
                    item -> {
                      Assertions.assertNotNull(item.getSexText());
                      Assertions.assertNotNull(item.getStatusText());
                    }));
  }

  @Test
  void resR() {
    var r = serviceTest.resR();
    System.out.println(r.getResult().getRecords());
    Assertions.assertNotNull(r.getResult());
    Assertions.assertFalse(r.getResult().getRecords().isEmpty());
  }
}

版权声明