[新增功能](dev): 常规更新

1. 增加工具类 TranslatorUtil (有时候不方便写 @Translator 注解)
2. 缓存触发翻译的字段(数组翻译时不用每次都去解析,算提升点速度吧)
3. 自定义翻译注解的 conditionField 字段支持写固定值。格式:V:<值>
This commit is contained in:
song_jx 2023-08-22 22:26:54 +08:00
parent 8f75fa8108
commit bb59361d5e
8 changed files with 127 additions and 33 deletions

View File

@ -60,6 +60,13 @@ public @interface Translate {
/**
* 判断条件字段仅自定义 dictionary 时有效
* <p>
* 该参数有两种形式定义如下
*
* <ul>
* <li>1. 值形式 填写格式为 V:{具体值} V:123 这样会直接将 123 传到翻译实现接口</li>
* <li>2. 字段形式 填写的是对象中的其他字段翻译前会先获取该字段的结果并将结果值传到翻译实现接口</li>
* </ul>
*/
String conditionField() default "";

View File

@ -1,12 +1,12 @@
package com.aizuda.trans.aspect;
import com.aizuda.trans.handler.TranslatorHandle;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Component;
@ -19,10 +19,10 @@ import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class TranslateAspect {
@Autowired
private GenericConversionService genericConversionService;
private final GenericConversionService genericConversionService;
@Pointcut("@annotation(com.aizuda.trans.annotation.Translator)")
public void pointCut() {

View File

@ -5,10 +5,12 @@ import com.aizuda.trans.service.SummaryExtractService;
import com.aizuda.trans.service.impl.DefaultDictTranslateServiceImpl;
import com.aizuda.trans.service.impl.DefaultSummaryExtractServiceImpl;
import com.aizuda.trans.service.impl.wrapper.IPageUnWrapper;
import com.aizuda.trans.util.TranslatorUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.support.GenericConversionService;
/**
* 翻译配置
@ -56,4 +58,16 @@ public class TranslatorConfig {
return new IPageUnWrapper<Object>();
}
/**
* 翻译工具类
*
* @param conversionService 转换服务
* @return {@link TranslatorUtil }
* @author nn200433
*/
@Bean
public TranslatorUtil translatorUtil(GenericConversionService conversionService) {
return new TranslatorUtil(conversionService);
}
}

View File

@ -1,9 +1,11 @@
package com.aizuda.trans.handler;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.*;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
@ -22,6 +24,7 @@ import com.aizuda.trans.summary.ISummaryExtract;
import com.aizuda.trans.util.NameUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import sun.reflect.annotation.AnnotationType;
import java.lang.annotation.Annotation;
@ -44,7 +47,12 @@ import static com.aizuda.trans.util.LambdaUtil.uncheck;
*/
@Slf4j
public class TranslatorHandle {
/** 条件字段是否为常量值的前缀 */
private static String CONDITION_FIELD_IS_VALUE_PREFIX = "V:";
/** 循环遍历缓存 */
private static Cache<String, List<Field>> LOOP_FIELD_CACHE = CacheUtil.newLFUCache(100);
/**
* 翻译Map或Entity或Page
*
@ -84,7 +92,7 @@ public class TranslatorHandle {
* @param <T> 支持Entity或者Map
* @return List
*/
public static <T> Collection<T> parse(Collection<T> originList) {
private static <T> Collection<T> parse(Collection<T> originList) {
if (CollUtil.isEmpty(originList)) {
return originList;
}
@ -93,18 +101,24 @@ public class TranslatorHandle {
final FormatType fieldFormatType = getFieldType(originList);
// 翻译数据
originList.forEach(bean -> {
originList.parallelStream().forEach(bean -> {
final Class<?> beanClass = bean.getClass();
/*
final Field[] declaredFields = ReflectUtil.getFields(beanClass);
// 循环处理需要转换的字段字段上的注解链上需要有@Transform且字段类型必须为String
Arrays.stream(declaredFields)
// 只转换简单值类型的属性会把值转为String类型处理其他类型的属性代表是嵌套情况需要过滤掉后面处理
.filter(field -> ClassUtil.isSimpleValueType(field.getType()) && AnnotationUtil.hasAnnotation(field, Translate.class))
.filter(field -> field.isAnnotationPresent(Translate.class) && ClassUtil.isSimpleValueType(field.getType()))
.forEach(field -> transformField(bean, fieldFormatType, field));
// 转换嵌套字段字段上需要标注@Transform且字段类型不为String递归转换
Arrays.stream(declaredFields)
.filter(field -> field.getType() != String.class && field.isAnnotationPresent(Translator.class))
.filter(field -> field.isAnnotationPresent(Translator.class) && !ClassUtil.isSimpleValueType(field.getType()))
.forEach(field -> transform(ReflectUtil.invoke(bean, ReflectUtil.getMethodByName(beanClass, "get" + StrUtil.upperFirst(field.getName())))));
*/
// 只转换简单值类型的属性会把值转为String类型处理其他类型的属性代表是嵌套情况需要过滤掉后面处理
getLoopFields(beanClass, Translate.class).forEach(field -> transformField(bean, fieldFormatType, field));
// 转换嵌套字段字段上需要标注@Transform且字段类型不为String递归转换
getLoopFields(beanClass, Translator.class).forEach(field -> transform(ReflectUtil.invoke(bean, ReflectUtil.getMethodByName(beanClass, "get" + StrUtil.upperFirst(field.getName())))));
});
return originList;
@ -119,10 +133,7 @@ public class TranslatorHandle {
* @return {@link List }<{@link Object }>
* @author nn200433
*/
public static List<Object> parse(String originValue, Dictionary dictionaryConfig, ExtendParam extendParam) {
if (originValue == null) {
return null;
}
private static List<Object> parse(String originValue, Dictionary dictionaryConfig, ExtendParam extendParam) {
final Translatable translator = getTranslatable(extendParam.getDictClass(), dictionaryConfig.translator());
return CollUtil.defaultIfEmpty(translator.translate(originValue, dictionaryConfig, extendParam), Collections.emptyList());
}
@ -157,10 +168,9 @@ public class TranslatorHandle {
}
// 获取条件字段值
String conditionFieldValue = null;
if (StrUtil.isNotBlank(conditionField)) {
conditionFieldValue = Convert.toStr(getProperty(origin, conditionField));
}
String conditionFieldValue = Opt.ofBlankAble(conditionField)
.map(c -> StrUtil.startWith(c, CONDITION_FIELD_IS_VALUE_PREFIX) ? StrUtil.removePrefix(c, CONDITION_FIELD_IS_VALUE_PREFIX) : Convert.toStr(getProperty(origin, c)))
.get();
// 获取翻译结果并脱敏处理
final ExtendParam extendParam = new ExtendParam(groupValue, conditionFieldValue, dictClass, maxLen);
@ -367,9 +377,9 @@ public class TranslatorHandle {
*/
private static Method getMethod(Class<?> clazz, String fieldName, String prefix) {
final String methodName = prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return Stream.of(ReflectUtil.getMethods(clazz, m -> m.getName().equals(methodName))).findAny()
.orElseThrow(() -> new IllegalArgumentException(
clazz.getSimpleName() + ".class中未添加翻译属性" + fieldName + "或其对应get/set方法"));
return Stream.of(ReflectUtil.getMethods(clazz, m -> m.getName().equals(methodName)))
.findAny()
.orElseThrow(() -> new IllegalArgumentException(clazz.getSimpleName() + ".class中未添加翻译属性" + fieldName + "或其对应get/set方法"));
}
/**
@ -388,8 +398,7 @@ public class TranslatorHandle {
}
// 否则进行脱敏操作
final DesensitizedUtil.DesensitizedType desensitizedType = EnumUtil.fromString(
DesensitizedUtil.DesensitizedType.class, desensitizedModel, null);
final DesensitizedUtil.DesensitizedType desensitizedType = EnumUtil.fromString(DesensitizedUtil.DesensitizedType.class, desensitizedModel, null);
if (null == desensitizedType && StrUtil.isWrap(desensitizedModel, StrUtil.DELIM_START, StrUtil.DELIM_END)) {
// 找不到匹配脱敏模型时用hide解析
final String model = StrUtil.unWrap(desensitizedModel, StrUtil.DELIM_START, StrUtil.DELIM_END);
@ -441,8 +450,40 @@ public class TranslatorHandle {
}
// Translatable 实现类上配置了 @Component 则使用 Spring 容器获取 否者 new 一个实现类
return translatorClass.isAnnotationPresent(Component.class) ? SpringUtil.getBean(translatorClass) : uncheck(translatorClass::newInstance);
return translatorClass.isAnnotationPresent(Service.class) || translatorClass.isAnnotationPresent(Component.class) ? SpringUtil.getBean(translatorClass) : uncheck(translatorClass::newInstance);
}
/**
* 获取循环字段
*
* @param beanClass bean类
* @param annotationClass 注释类
* @return {@link List }<{@link Field }>
* @author nn200433
*/
private static List<Field> getLoopFields(Class<?> beanClass, Class<? extends Annotation> annotationClass) {
final String key = beanClass.getName() + StrUtil.UNDERLINE + annotationClass.getName();
List<Field> resultFieldList = LOOP_FIELD_CACHE.get(key);
if (ObjUtil.isNull(resultFieldList)) {
final Field[] declaredFields = ReflectUtil.getFields(beanClass);
if (ClassUtil.isAssignable(Translate.class, annotationClass)) {
log.debug("---> {} 初始化要翻译的字段缓存", key);
// 字段翻译
resultFieldList = Arrays.stream(declaredFields)
// 过滤出简单类型简单数组类型Collection实现类
.filter(f -> f.isAnnotationPresent(Translate.class) && (ClassUtil.isSimpleValueType(f.getType()) || ClassUtil.isSimpleTypeOrArray(f.getType()) || ClassUtil.isAssignable(Collection.class, f.getType())))
.collect(Collectors.toList());
} else {
log.debug("---> {} 初始化要嵌套翻译的字段缓存", key);
// 嵌套翻译
resultFieldList = Arrays.stream(declaredFields)
.filter(f -> f.isAnnotationPresent(Translator.class) && !ClassUtil.isSimpleValueType(f.getType()))
.collect(Collectors.toList());
}
LOOP_FIELD_CACHE.put(key, resultFieldList);
}
return resultFieldList;
}
}

View File

@ -15,8 +15,8 @@ import com.aizuda.trans.enums.FormatType;
import com.aizuda.trans.service.Translatable;
import com.aizuda.trans.util.NameUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@ -35,13 +35,13 @@ import java.util.stream.Collectors;
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DataBaseTranslator implements Translatable {
/**
* 获取数据源
*/
@Autowired(required = false)
private DataSource dataSource;
private final DataSource dataSource;
@Override
public List<Object> translate(String origin, Dictionary dictConfig, ExtendParam extendParam) {

View File

@ -4,8 +4,8 @@ import com.aizuda.trans.annotation.Dictionary;
import com.aizuda.trans.entity.ExtendParam;
import com.aizuda.trans.service.DictTranslateService;
import com.aizuda.trans.service.Translatable;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
@ -19,10 +19,10 @@ import java.util.List;
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DictCacheTranslator implements Translatable {
@Autowired
private DictTranslateService dictTranslateService;
private final DictTranslateService dictTranslateService;
@Override
public List<Object> translate(String origin, Dictionary dictConfig, ExtendParam extendParam) {

View File

@ -4,7 +4,7 @@ import com.aizuda.trans.annotation.Dictionary;
import com.aizuda.trans.entity.ExtendParam;
import com.aizuda.trans.service.SummaryExtractService;
import com.aizuda.trans.service.Translatable;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Collections;
@ -17,10 +17,10 @@ import java.util.List;
* @date 2023-06-07 09:32:49
*/
@Component
@RequiredArgsConstructor
public class SummaryExtractTranslator implements Translatable {
@Autowired
private SummaryExtractService summaryExtractService;
private final SummaryExtractService summaryExtractService;
@Override
public List<Object> translate(String origin, Dictionary dictConfig, ExtendParam extendParam) {

View File

@ -0,0 +1,32 @@
package com.aizuda.trans.util;
import com.aizuda.trans.handler.TranslatorHandle;
import org.springframework.core.convert.support.GenericConversionService;
/**
* 翻译工具类
*
* @author nn200433
* @date 2023-08-01 001 16:28:54
*/
public class TranslatorUtil {
private static GenericConversionService genericConversionService;
public TranslatorUtil(GenericConversionService conversionService) {
genericConversionService = conversionService;
}
/**
* 翻译
* <p>
* 会先进行解对象比如 Ipage 对象只会提取 Records 进行翻译
*
* @param origin 源对象
* @author nn200433
*/
public static void transform(Object origin) {
TranslatorHandle.transform(genericConversionService.convert(origin, Object.class));
}
}