可能有很多友友们针对某些下拉框,单选框,多选框,设计表的时候会设计value和Label两个字段。
但考虑到可能字典描述可能会变,又不想查出数据后再去转一次。
这时候可以考虑通过自定义切面的方式,动态添加返回字段。
1、定义自定义注解
@JacksonAnnotationsInside
@JsonSerialize(using = DicTransContextualSerializer.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EmbedTrans {
/**
* 转译输出字段,若不配置,会在源字段名后面加Name
* 如:sex->sexName
*
* @return
*/
String fieldName() default "";
/**
* 需要转换的字典类型
*
* @return
*/
SysDictTypeEnum dicMetohd();
}
2、定义序列化类,具体的转换在【DicTransIntegerSerializer.serialize】里面
public class DicTransContextualSerializer extends JsonSerializer<Objects> implements ContextualSerializer {
private static final String DISPLAY_SUFFIX = "Desc";
@Autowired
private DictTranContext dictTranContext;
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
if (Objects.equals(beanProperty.getType().getRawClass(), Integer.class)
|| Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
EmbedTrans t = beanProperty.getAnnotation(EmbedTrans.class);
if (t != null) {
String beanFieldName = beanProperty.getName();
if (StringUtils.hasText(t.fieldName())) {
beanFieldName = t.fieldName();
} else {
beanFieldName = beanProperty.getName() + DISPLAY_SUFFIX;
}
//SysDictTypeEnum dicTypeEnum = SysDictTypeEnum.NORMAL_CODE_TRANS_NAME;
SysDictTypeEnum dicTypeEnum = null;
if (t.dicMetohd() != null) {
dicTypeEnum = t.dicMetohd();
}
//这个位置目前测试没啥问题
if (Objects.equals(beanProperty.getType().getRawClass(), Integer.class)) {
//DicInterface.bel();
return new DicTransIntegerSerializer(dictTranContext, beanFieldName, dicTypeEnum);
} else {
//DicTransStringSerializer
return new DicTransIntegerSerializer(dictTranContext, beanFieldName, dicTypeEnum);
}
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(beanProperty);
}
@Override
public void serialize(Objects s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
}
}
public class DicTransIntegerSerializer extends JsonSerializer<Integer> {
//注入失败
// @Autowired
// private DictTranContext dictTranContext;
//private DictTranContext dictTranContext =
// (DictTranContext) SpringUtil.getBean("DictTranContext");
private DictTranContext dictTranContext;
private String fieldName;
private SysDictTypeEnum dicTypeEnum;
public DicTransIntegerSerializer(DictTranContext dictTranContext, String fieldName, SysDictTypeEnum dicTypeEnum) {
this.fieldName = fieldName;
this.dicTypeEnum = dicTypeEnum;
this.dictTranContext = dictTranContext;
}
public DicTransIntegerSerializer() {
}
@Override
public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeObject(integer);
jsonGenerator.writeFieldName(fieldName);
//TODO 写自己具体逻辑
if (ObjectUtil.isNotEmpty(integer)) {
DictTranTemplate dictTranTemplate = dictTranContext.getTemplateInstance();
String dictLabel = dictTranTemplate.dictConver(dicTypeEnum, String.valueOf(integer));
if (ObjectUtil.isNotEmpty(dictLabel)) {
jsonGenerator.writeString(dictLabel);
return;
}
}
jsonGenerator.writeString("");
}
}
3.配置注解,指定转换字段
【@EmbedTrans(dicMetohd = SysDictTypeEnum.COMMON_ENABLE)】
public class SysUserQueryVO {
@ApiModelProperty("主键ID")
private Integer id;
@ApiModelProperty("主键ID")
private String username;
@ApiModelProperty("是否启用(0启用,1禁用)")
//@DictLabel(dictType = SysDictTypeEnum.COMMON_ENABLE)
@EmbedTrans(dicMetohd = SysDictTypeEnum.COMMON_ENABLE)
private Integer enable;
@ApiModelProperty(value = "姓名")
@SensitiveData(strategy = SensitiveEnum.USERNAME)
private String nickname;
@ApiModelProperty(value = "身份证")
@SensitiveData(strategy = SensitiveEnum.ID_CARD)
private String idCardNum;
@ApiModelProperty(value = "联系电话")
private String mobile;
@ApiModelProperty(value = "门店id")
private Integer storeId;
}
正常来说完成上面步骤就可以针对字典值字段动态添加字段描述字段返回给前端。
但是友友们的项目可能微服务架构,数据库db层和逻辑层service不在一个项目中,db无法使用service层的字典转换方法(可能也要查咖啡因一级缓存和redis二级缓存),后面就用了设计模式的思想进一步封装。(可能我实现比较复杂)
4、自定义【DictTranAnno】注解,用在springboot启动时扫描该注解,缓存到map中后续直接使用
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DictTranAnno {
// 转换类型
String tranType() default "default";
}
5、定义上下文,用来扫描【DictTranAnno】注解
@Slf4j
@Component
public class DictTranContext implements ApplicationContextAware {
@Resource
private ApplicationContext applicationContext;
private static final Map<String, Class<?>> templateMap = new HashMap<>(8);
public DictTranTemplate getTemplateInstance() {
Class<?> clazz = templateMap.get("default");
if (clazz == null) {
throw new RuntimeException("默认字典转换处理不存在");
}
return (DictTranTemplate) this.applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
log.info(">>>>>>>>>>>>>>>>>>>>>>>开始加载字典转换>>>>>>>>>>>>>>>>>>>>>>>");
// 遍历带有DictTranAnno注释的类
Map<String, Object> beans = applicationContext.getBeansWithAnnotation(DictTranAnno.class);
if (beans != null && beans.size() > 0) {
//TODO 判断是否有相同tranType,有的话则抛异常
for (Object serviceBean : beans.values()) {
String tranType = serviceBean.getClass().getAnnotation(DictTranAnno.class).tranType();
templateMap.put(tranType, serviceBean.getClass());
}
}
log.info(">>>>>>>>>>>>>>>>>>>>>>>加载字典转换完成>>>>>>>>>>>>>>>>>>>>>>>");
log.info(">>>>>>>>>>>>>>>>>>>>>>>" + templateMap.toString() + ">>>>>>>>>>>>>>>>>>>>>>>");
}
}
6、在DB项目定义抽象类,用来实现具体转换
@Service
public abstract class DictTranTemplate {
/**
* 用来转换字典的具体实现
* @param dicTypeEnum
* @param dictValue
* @return
*/
protected abstract String dictTran(SysDictTypeEnum dicTypeEnum, String dictValue);
/**
* 抛出给调用者的方法
* @param dicTypeEnum
* @param dictValue
* @return
*/
public String dictConver(SysDictTypeEnum dicTypeEnum, String dictValue) {
return dictTran(dicTypeEnum, dictValue);
}
}
【DicTransIntegerSerializer.serialize】中具体实现代码为
7、在Service项目定义实现类继承模板类【DictTranTemplate】
@Service
@DictTranAnno
public class DicTranSerializer extends DictTranTemplate {
@Autowired
private ISysDictDataService dataService;
@Override
protected String dictTran(SysDictTypeEnum dicTypeEnum, String dictValue) {
//自己的转换实现
return dataService.getDictLabelRedisByValue(dicTypeEnum.getCode(), dictValue);
}
}
至此就完成啦,实现的可能并不是很完美,但也学到了设计模式的使用。
有不清楚的地方欢迎评论~