背景:
项目中经常会遇到需要把某些字典值翻译给前端,我们所有的字典值往往存在一个单独的表里,在业务表中只存储对应的code码,需要使用时则根据code码去查找对应字典项的名称。例如:我们的字典表为dict,表结构如下:
其中code就是字典项code码,name是字典项对应的名称,type是该字典值类型。
我们在业务表person中有个字段sex,代表性别,我们在该表中只存储其code值,需要用时才会根据code 值和type值查询其对应的名称,如下所示:
person表
dict表:
如果每次都手动查询dict表返回给前端太麻烦,有时项目中会缓存全量的字典值,需要用时从缓存中取值,这样做一方面会损耗系统性能,另一方面也不具有实时性。所以,我们需要对其进行改造,实现以下效果:
1、每次返回给前端code值时会自动去数据库查询对应的name值,而不需要手动处理。
2、支持通过注解设置返回给前端的字段名。
3、可以指定配置生效的controller,而对于未配置的controller不会生效。
解决方案:
1、创建自定义注解@DictField,
@JacksonAnnotationsInside @JsonSerialize(using = DictSerializer.class) @Retention(RetentionPolicy.RUNTIME) public @interface DictField { //返回字段的字段名 String name() default ""; //字段在字典表中的类型 String value(); }
2、编写序列化处理逻辑
public class DictSerializer extends JsonSerializer<String> implements ContextualSerializer { private DictField dictField; private BeanProperty beanProperty; public DictSerializer(DictField dictField, BeanProperty beanProperty) { this.dictField = dictField; this.beanProperty = beanProperty; } public DictSerializer() { } public static String getDictName(String code,String type) { TransitSupportDictService transitSupportDictService = ApplicationContextUtil.getBean(TransitSupportDictService.class); return transitSupportDictService.getTypeName(code,type); } @Override //数据序列化时,会执行此方法,value是标注@DictField注解的字段值,即code码,通过getDictName(value,dictField.value())方法 //根据字段类型dictField.value()和code码查询出该字段名称,设置到字段名dictField.name()上。 public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeString(value); String dictName = getDictName(value,dictField.value()); if (StringUtils.isNotEmpty(dictName)) { String name = dictField.name(); if (name.length() == 0) { name = beanProperty.getName() + "_mc"; } jsonGenerator.writeStringField(name, dictName); } } @Override public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException { if (beanProperty != null) { DictField dictField = beanProperty.getAnnotation(DictField.class); if (dictField != null) { return new DictSerializer(dictField, beanProperty); } else { return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty); } } return serializerProvider.findNullValueSerializer(null); } }
3、使用自定义注解
4.指定生效controller
由于我们项目中底层做了重写,默认使用fastjson,所以需要重新做拦截,针对指定的controller 改用jackjson我们之前的配置才会生效。
@ControllerAdvice public class TransitSupportDictResponseBodyAdvice implements ResponseBodyAdvice<Object> { private final MappingJackson2HttpMessageConverter converter; public TransitSupportDictResponseBodyAdvice() { converter = buildConverter(); } private MappingJackson2HttpMessageConverter buildConverter() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); ObjectMapper objectMapper = builder .indentOutput(true) .simpleDateFormat("yyyy-MM-dd HH:mm:ss") .timeZone(TimeZone.getTimeZone("GMT+08:00")) .mixIn(ResponseMessage.class, ResponseMessageIgnore.class) .build(); return new MappingJackson2HttpMessageConverter(objectMapper); } @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { //只有配置了的controller 才会生效 return (returnType.getDeclaringClass().getPackage().getName().startsWith(transitSupportCommonProperties.getBasePackages()) && returnType.getMethod().getReturnType() == ResponseMessage.class) } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body == null) { return null; } try { converter.write(body, returnType.getGenericParameterType(), MediaType.APPLICATION_JSON_UTF8, response); } catch (IOException ignore) { } return null; }
5、最终效果
至此,即可实现自动化翻译项目中的字典值。