上一篇,自定义注解+拦截器,实现字段加解密操作,奈何公司的这个项目里没有字典值翻译的功能,正好可以再自定义注解+拦截器方式的基础上,扩展一下
第一步,新建一个注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
//对应数据字典的code
String dictCode();
}
第二步,新建拦截器
@Component
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class DictInterceptor implements Interceptor {
@Resource
private DictInfoService dictInfoService;
@Override
public Object intercept(Invocation invocation) throws Throwable {
//取出查询的结果
Object resultObject = invocation.proceed();
if (null != resultObject) {
// 对结果中的字典值进行翻译
dictInfoService.parseDictValue(resultObject);
}
return resultObject;
}
}
第三步,新建接口
public interface DictInfoService {
/**
* 翻译字典值
*
* @param result 待翻译字段外层对象
*/
<T> T parseDictValue(T result);
}
第四步,新建实现类
@Service
public class DictInfoServiceImpl implements DictInfoService {
public static final Logger log = LoggerFactory.getLogger(DictInfoServiceImpl.class);
public static final String suffix = "DictName";
@Override
public <T> T parseDictValue(T result) {
// 返回需要翻译字段
List<Field> fields = this.checkAndGetFields(result);
// 解密字段不为空,解密
if (!CollectionUtils.isEmpty(fields)) {
for (int i = 0; i < fields.size(); i++) {
// 解密
T translation = this.translation(fields.get(i), result);
result= translation;
}
}
return result;
}
/**
* 返回需要翻译字段
*/
private <T> List<Field> checkAndGetFields(T t) {
// 数据为空直接返回
if (null == t) {
return null;
}
if (t instanceof Collection) {
// 如果是集合,返回集合中元素的类中需要加解密的敏感信息字段
Collection<?> rc = (Collection<?>) t;
if (!CollectionUtils.isEmpty(rc)) {
Object next = rc.iterator().next();
if (null != next) {
return ReflectUtils.getAllField(next.getClass(), Dict.class);
}
}
} else {
// 返回需要加解密的敏感信息字段
return ReflectUtils.getAllField(t.getClass(), Dict.class);
}
return null;
}
/**
* 翻译
*/
private <T> T translation(Field field, Object o) {
// 如果是集合,则遍历出实体,逐个解密
if (o instanceof Collection) {
List<Object> originalList = (List<Object>) o;
ExecutorService executor = ForkJoinPool.commonPool(); // 使用公共的ForkJoinPool
List<Future<Object>> futures = new ArrayList<>(originalList.size());
for (int i = 0; i < originalList.size(); i++) {
Object originalElement = originalList.get(i);
Future<Object> future = executor.submit(() -> {
Object transformedElement = this.translationObj(field, originalElement);
return transformedElement;
});
futures.add(future);
}
for (int i = 0; i < futures.size(); i++) {
try {
Object transformedElement = futures.get(i).get();
originalList.set(i, transformedElement);
} catch (InterruptedException | ExecutionException e) {
// 处理异常
e.printStackTrace();
}
}
executor.shutdown(); // 关闭线程池
} else {
o=this.translationObj(field, o);
}
return (T) o;
}
/**
* 对象中字段解密
*/
private Object translationObj(Field field, Object o) {
try {
// 访问private变量的变量值
field.setAccessible(true);
// 字段值
Object value = field.get(o);
Dict dict = field.getAnnotation(Dict.class);
if(dict!=null){
//从数据字典中查询code对应的k-v值
List<SysDictData> dictDatas = DictUtils.getDictCache(dict.dictCode());
if(dictDatas!=null){
Map<String, String> dictMap = dictDatas.stream()
.collect(Collectors.toMap(
SysDictData::getDictValue, // key
SysDictData::getDictLabel // value
));
//字段转换
String key = field.getName();
String convertValue = dictMap.get(value);
String json = configureObjectMapper().writeValueAsString(o);
JSONObject jsonObject = JSONObject.parseObject(json);
jsonObject.put(field.getName()+suffix,convertValue);
if(StringUtils.isNotEmpty(jsonObject.toString())){
o = configureObjectMapper().readValue(jsonObject.toString(), o.getClass());
}
}
}
} catch (Exception e) {
log.error("字段翻译失败,类={}, 字段={}", o.getClass().getName(), field.getName(), e);
}
return o;
}
public static ObjectMapper configureObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 添加对Java 8时间API的支持
objectMapper.registerModule(new JavaTimeModule());
SimpleModule module = new SimpleModule()
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
objectMapper.registerModule(module);
return objectMapper;
}
}
自定义序列化器
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public void serialize(LocalDateTime localDateTime, JsonGenerator gen, SerializerProvider serializers) throws IOException {
String formattedDateTime = localDateTime.format(FORMATTER);
gen.writeString(formattedDateTime);
}
}
由于在于ObjectMapper默认不支持Java 8的时间类型LocalDateTime。还需要显式地添加对Java 8时间API的支持。
添加依赖:确保项目中引入了jackson-datatype-jsr310库,这个库提供了对Java 8时间API的支持。
注册模块:除了自定义序列化器外,还需注册JavaTimeModule或相应的自定义模块。
第五步,应用
要查询实体类中的字段上加上注解
/**
* 性别
*/
@Schema(description = "性别 ")
@TableField("gender")
@Dict(dictCode = "sys_user_sex")
private String gender;
/**
* 性别名称
*/
@TableField(exist = false)
private String genderDictName;
/**
* 民族
*/
@Schema(description = "民族")
@TableField("ethnicity")
@Dict(dictCode = "zc_ethnic_group")
private String ethnicity;
/**
* 民族名称
*/
@TableField(exist = false)
private String ethnicityDictName;
查询结果