代码完善(dev): 代码完善

1. 新增配置:在将json字符串数据转为对象时,将字符串数据置空(默认开启)
2.
翻译结果写入字段提成接口,内置默认实现。(可自定义实现,实现`WriteFieldNameService`接口即可)
3.
修复高版本jdk报错
4. Hutool、Mybatis-plus 升级
This commit is contained in:
song_jx 2024-01-29 14:22:57 +08:00
parent 91b7ab47f5
commit bd20549773
12 changed files with 271 additions and 70 deletions

View File

@ -0,0 +1,48 @@
package com.aizuda.trans.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
/**
* 注解 构造器
*
* @author nn200433
* @date 2024-01-29 01:41:01
*/
public class AnnotationBuilder {
private final Class<? extends Annotation> annotationType;
private final Map<String, Object> values = new HashMap<String, Object>();
public AnnotationBuilder(Class<? extends Annotation> annotationType) {
this.annotationType = annotationType;
}
/**
* 设值
*
* @param methodName 方法名称
* @param value 价值
* @author nn200433
*/
public void setValue(String methodName, Object value) {
values.put(methodName, value);
}
/**
* 构建
*
* @return {@link Annotation }
* @author nn200433
*/
public Annotation build() {
return (Annotation) Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class[]{annotationType},
new AnnotationProxyBuilder(values)
);
}
}

View File

@ -0,0 +1,30 @@
package com.aizuda.trans.annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 注解代理生成器
*
* @author nn200433
* @date 2024-01-29 01:41:17
*/
public class AnnotationProxyBuilder implements InvocationHandler {
private final Map<String, Object> values;
public AnnotationProxyBuilder(Map<String, Object> values) {
this.values = values;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (values.containsKey(methodName)) {
return values.get(methodName);
}
return method.getDefaultValue();
}
}

View File

@ -2,7 +2,11 @@ package com.aizuda.trans.annotation;
import com.aizuda.trans.constants.DesensitizedTypeConstants;
import java.lang.annotation.*;
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;
/**
* 字典翻译注解标识在需要翻译的字段上
@ -79,6 +83,11 @@ public @interface Translate {
*/
String desensitizedModel() default DesensitizedTypeConstants.EMPTY;
/**
* 在将json字符串数据转为对象时将字符串数据置空默认开启
*/
boolean setFieldEmpty() default true;
/**
* 最大长度限制
* 适用于以下翻译

View File

@ -1,9 +1,13 @@
package com.aizuda.trans.annotation;
import java.lang.annotation.*;
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 luozhan
* @date 2020-04

View File

@ -0,0 +1,27 @@
package com.aizuda.trans.service;
import com.aizuda.trans.annotation.Translate;
import com.aizuda.trans.enums.FormatType;
import java.util.List;
/**
* 写入字段名服务
*
* @author nn200433
* @date 2024-01-29 01:50:54
*/
public interface WriteFieldNameService {
/**
* 获取领域名称列表
*
* @param translateConfig 翻译配置
* @param originFieldName 原点字段名称
* @param fieldFormatType 字段格式类型
* @return {@link List }<{@link String }>
* @author nn200433
*/
public List<String> getFieldNameList(Translate translateConfig, String originFieldName, FormatType fieldFormatType);
}

View File

@ -25,7 +25,7 @@ public class People3 {
private String name;
@Translate(dictClass = JSONConvert.class, translateField = "jsonObj")
@Translate(dictClass = JSONConvert.class, translateField = "jsonObj", setFieldEmpty = true)
private String json;
private Object jsonObj;

View File

@ -4,7 +4,12 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.json.JSONUtil;
import com.aizuda.trans.demo.DemoService;
import com.aizuda.trans.entity.*;
import com.aizuda.trans.entity.Device;
import com.aizuda.trans.entity.People;
import com.aizuda.trans.entity.People2;
import com.aizuda.trans.entity.People3;
import com.aizuda.trans.entity.People4;
import com.aizuda.trans.entity.Result;
import com.aizuda.trans.util.TranslatorUtil;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -2,8 +2,10 @@ package com.aizuda.trans.config;
import com.aizuda.trans.service.DictTranslateService;
import com.aizuda.trans.service.SummaryExtractService;
import com.aizuda.trans.service.WriteFieldNameService;
import com.aizuda.trans.service.impl.DefaultDictTranslateServiceImpl;
import com.aizuda.trans.service.impl.DefaultSummaryExtractServiceImpl;
import com.aizuda.trans.service.impl.defaults.DefaultWriteFieldNameServiceImpl;
import com.aizuda.trans.service.impl.wrapper.IPageUnWrapper;
import com.aizuda.trans.util.TranslatorUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -46,6 +48,18 @@ public class TranslatorConfig {
return new DefaultSummaryExtractServiceImpl();
}
/**
* 注册写入字段服务默认实现
*
* @return {@link WriteFieldNameService }
* @author nn200433
*/
@Bean
@ConditionalOnMissingBean
public WriteFieldNameService writeFieldNameService() {
return new DefaultWriteFieldNameServiceImpl();
}
/**
* 注册IPage解包器

View File

@ -6,6 +6,8 @@ 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.map.WeakConcurrentMap;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.EnumUtil;
@ -14,6 +16,7 @@ import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.aizuda.trans.annotation.AnnotationBuilder;
import com.aizuda.trans.annotation.Dictionary;
import com.aizuda.trans.annotation.Translate;
import com.aizuda.trans.annotation.Translator;
@ -24,6 +27,7 @@ import com.aizuda.trans.enums.FormatType;
import com.aizuda.trans.enums.IEnum;
import com.aizuda.trans.json.IJsonConvert;
import com.aizuda.trans.service.Translatable;
import com.aizuda.trans.service.WriteFieldNameService;
import com.aizuda.trans.service.impl.DataBaseTranslator;
import com.aizuda.trans.service.impl.DesensitizedTranslator;
import com.aizuda.trans.service.impl.DictCacheTranslator;
@ -35,15 +39,14 @@ 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;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -64,9 +67,11 @@ import static com.aizuda.trans.util.LambdaUtil.uncheck;
public class TranslatorHandle {
/** 条件字段是否为常量值的前缀 */
private static String CONDITION_FIELD_IS_VALUE_PREFIX = "V:";
private static String CONDITION_FIELD_IS_VALUE_PREFIX = "V:";
/** 循环遍历缓存 */
private static Cache<String, List<Field>> LOOP_FIELD_CACHE = CacheUtil.newLFUCache(100);
private static Cache<String, List<Field>> LOOP_FIELD_CACHE = CacheUtil.newLFUCache(100);
/** 方法缓存 */
private static final WeakConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakConcurrentMap<>();
/**
* 翻译Map或Entity或Page
@ -172,13 +177,14 @@ public class TranslatorHandle {
// 2.获取每个待翻译属性的配置
final Translate translateConfig = field.getAnnotation(Translate.class);
final Class<?> dictClass = getDictClass(translateConfig);
final List<String> writeFieldList = NameUtil.parseCamelTo(getTranslateFieldName(translateConfig, fName), fieldFormatType);
final List<String> writeFieldList = getWriteFieldNameSrv().getFieldNameList(translateConfig, fName, fieldFormatType);
final String groupValue = translateConfig.groupValue();
final String conditionField = translateConfig.conditionField();
final String desensitizedModel = translateConfig.desensitizedModel();
final int maxLen = translateConfig.maxLen();
final Dictionary dictionaryConfig = handle(dictClass, translateConfig);
final boolean isJsonConvert = IJsonConvert.class.isAssignableFrom(dictClass);
final boolean isSetFieldEmpty = translateConfig.setFieldEmpty();
// 获取未翻译的原值如果值为空则跳过
String originValue = Convert.toStr(getProperty(origin, fieldName));
@ -200,7 +206,7 @@ public class TranslatorHandle {
}
// 将翻译结果填入翻译展示字段
setProperty(origin, isJsonConvert, fieldName, writeFieldList, translateValList);
setProperty(origin, (isJsonConvert && isSetFieldEmpty), fieldName, writeFieldList, translateValList);
}
/**
@ -256,8 +262,8 @@ public class TranslatorHandle {
/**
* 将注解属性填充到另一个相同类型的注解中目标注解中已经存在属性值的不会被覆盖
*
* @param annotationFrom
* @param annotationTo
* @param annotationFrom 用户自定义的注解值
* @param annotationTo 默认的注解值
* @return 返回annotationTo如果annotationTo为空返回annotationFrom
*/
private static Annotation joinAnnotationValue(Annotation annotationFrom, Annotation annotationTo) {
@ -268,70 +274,51 @@ public class TranslatorHandle {
if (annotationFrom == null) {
return annotationTo;
}
Object handlerFrom = Proxy.getInvocationHandler(annotationFrom);
Object handlerTo = Proxy.getInvocationHandler(annotationTo);
Field fieldFrom = uncheck(() -> ReflectUtil.getField(handlerFrom.getClass(), "memberValues"));
Field fieldTo = uncheck(() -> ReflectUtil.getField(handlerTo.getClass(), "memberValues"));
fieldFrom.setAccessible(true);
fieldTo.setAccessible(true);
Map<String, Object> memberValuesFrom = uncheck(() -> (Map) fieldFrom.get(handlerFrom));
Map<String, Object> memberValuesTo = uncheck(() -> (Map) fieldTo.get(handlerTo));
// 注解默认值注意不会包含没有默认值的属性
Map<String, Object> defaultValueMap = AnnotationType.getInstance(annotationTo.annotationType())
.memberDefaults();
// 若目标注解中全都是默认值代表没有设置则直接返回原注解
// 否则属性填充后会直接改变目标注解的默认值影响其他引用的地方
if (defaultValueMap.equals(memberValuesTo)) {
return annotationFrom;
}
// 如果目标注解属性未设置则往目标里填充值
memberValuesTo.forEach((field, value) -> {
if (value.equals(defaultValueMap.get(field))) {
memberValuesTo.put(field, memberValuesFrom.get(field));
final Class<? extends Annotation> annotationType = annotationTo.annotationType();
Method[] methods = METHODS_CACHE.computeIfAbsent(annotationType, () -> annotationType.getDeclaredMethods());
AnnotationBuilder builder = new AnnotationBuilder(annotationType);
for (Method method : methods) {
try {
final String name = method.getName();
final Object defaultValue = method.getDefaultValue();
final Object valueFrom = method.invoke(annotationFrom);
final Object valueTo = method.invoke(annotationTo);
Object newV = defaultValue;
if (valueTo == null || valueTo.equals(defaultValue) || ArrayUtil.equals(valueTo, defaultValue)) {
// 如果目标注解中的值为空或者等于默认值则使用来源注解的值
newV = valueFrom;
}
builder.setValue(name, newV);
} catch (Exception e) {
// 处理异常具体情况可以根据实际需求进行调整
log.error("", e);
}
});
return annotationTo;
}
Annotation build = builder.build();
return build;
}
/**
* 若注解中未配置translateField则默认将原属性名的Id或Code字样替换成Name
* <p>
* resTypeId / resTypeCode -> staffName
* 注解成员默认值
*
* @param translateConfig 转换配置
* @param originFieldName 原始字段名称
* @return {@link List }<{@link String }>
* @param annotationTo 注释转换为
* @return {@link Map }<{@link String }, {@link Object }>
* @author nn200433
*/
private static List<String> getTranslateFieldName(Translate translateConfig, String originFieldName) {
final Class<?> dictClass = translateConfig.dictClass();
final String newName = originFieldName.replaceFirst("(Id|Code)$|$", "Name");
String[] translateFieldArray = translateConfig.translateField();
final int translateFieldLen = translateFieldArray.length;
if (translateFieldLen == 0) {
if (IDesensitized.class.isAssignableFrom(dictClass) || ISummaryExtract.class.isAssignableFrom(dictClass)) {
// 脱敏与摘要提取功能没配置翻译字段直接返回原字段即替换原始数据
translateFieldArray = new String[]{originFieldName};
} else {
// 否则返回额外提供的翻译字段
translateFieldArray = new String[]{newName};
}
} else {
for (int i = 0; i < translateFieldLen; i++) {
final String translateField = translateFieldArray[i];
if (StrUtil.isBlank(translateField)) {
translateFieldArray[i] = newName + i;
}
private static Map<String, Object> annotationMemberDefaults (Annotation annotationTo) {
final Class<? extends Annotation> annotationType = annotationTo.annotationType();
final Method[] methods = METHODS_CACHE.computeIfAbsent(annotationType, () -> annotationType.getDeclaredMethods());
Map<String, Object> defaultValueMap = new HashMap<String, Object>(methods.length);
for (Method method : methods) {
try {
defaultValueMap.put(method.getName(), method.getDefaultValue());
} catch (Exception e) {
defaultValueMap.put(method.getName(), null);
}
}
return CollUtil.newArrayList(translateFieldArray);
return defaultValueMap;
}
/**
* 根据对象获取属性
*
@ -381,6 +368,10 @@ public class TranslatorHandle {
} else {
// 此处不会抛异常
uncheck(() -> getMethod(o.getClass(), writeFieldName, "set").invoke(o, value));
if (isRemoveOriginField) {
// 字段内容置空
uncheck(() -> getMethod(o.getClass(), originFieldName, "set").invoke(o, StrUtil.EMPTY));
}
}
}
}
@ -504,5 +495,15 @@ public class TranslatorHandle {
return resultFieldList;
}
/**
* 获取翻译结果写入字段名称服务
*
* @return {@link WriteFieldNameService }
* @author nn200433
*/
private static WriteFieldNameService getWriteFieldNameSrv() {
return SpringUtil.getBean(WriteFieldNameService.class);
}
}

View File

@ -0,0 +1,62 @@
package com.aizuda.trans.service.impl.defaults;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.aizuda.trans.annotation.Translate;
import com.aizuda.trans.desensitized.IDesensitized;
import com.aizuda.trans.enums.FormatType;
import com.aizuda.trans.service.WriteFieldNameService;
import com.aizuda.trans.summary.ISummaryExtract;
import com.aizuda.trans.util.NameUtil;
import java.util.List;
/**
* 默认写入字段名称服务实现
*
* @author nn200433
* @date 2024-01-29 02:04:36
*/
public class DefaultWriteFieldNameServiceImpl implements WriteFieldNameService {
@Override
public List<String> getFieldNameList(Translate translateConfig, String originFieldName,
FormatType fieldFormatType) {
return NameUtil.parseCamelTo(getTranslateFieldName(translateConfig, originFieldName), fieldFormatType);
}
/**
* 若注解中未配置translateField则默认将原属性名的Id或Code字样替换成Name
* <p>
* resTypeId / resTypeCode -> staffName
*
* @param translateConfig 转换配置
* @param originFieldName 原始字段名称
* @return {@link List }<{@link String }>
* @author nn200433
*/
private List<String> getTranslateFieldName(Translate translateConfig, String originFieldName) {
final Class<?> dictClass = translateConfig.dictClass();
final String newName = originFieldName.replaceFirst("(Id|Code)$|$", "Name");
String[] translateFieldArray = translateConfig.translateField();
final int translateFieldLen = translateFieldArray.length;
if (translateFieldLen == 0) {
if (IDesensitized.class.isAssignableFrom(dictClass) || ISummaryExtract.class.isAssignableFrom(dictClass)) {
// 脱敏与摘要提取功能没配置翻译字段直接返回原字段即替换原始数据
translateFieldArray = new String[]{originFieldName};
} else {
// 否则返回额外提供的翻译字段
translateFieldArray = new String[]{newName};
}
} else {
for (int i = 0; i < translateFieldLen; i++) {
final String translateField = translateFieldArray[i];
if (StrUtil.isBlank(translateField)) {
translateFieldArray[i] = newName + i;
}
}
}
return CollUtil.newArrayList(translateFieldArray);
}
}

View File

@ -24,9 +24,9 @@
<!-- 版本 -->
<revision>0.4</revision>
<!-- 数据库操作 -->
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<mybatis-plus.version>3.5.4.1</mybatis-plus.version>
<!-- hutool -->
<hutool.version>5.8.21</hutool.version>
<hutool.version>5.8.24</hutool.version>
<!-- mysql -->
<mysql.version>5.1.49</mysql.version>
<!-- maven插件 -->