[代码完善](dev):

1. 主要增加json字符串翻译
2. 代码格式化 + 文档补充
This commit is contained in:
song_jx 2023-04-20 22:56:33 +08:00
parent 2fba3b25ed
commit f2305c3543
30 changed files with 469 additions and 264 deletions

View File

@ -41,16 +41,21 @@ EasyTranslate
│ │ ├── EnumPool.java
│ │ ├── FormatType.java
│ │ └── IEnum.java
│ ├── handler 主要操作类
│ │ └── TranslatorHandle.java
│ ├── json json相关
│ │ ├── IJsonConvert.java
│ │ └── JSONConvert.java
│ ├── service
│ │ ├── DictTranslateService.java 字典翻译接口
│ │ ├── impl
│ │ │ ├── DefaultDictTranslateServiceImpl.java 默认数据字典翻译实现(实现字典翻译接口。仿照该方法,实现自己的业务)
│ │ │ ├── DictCacheTranslator.java 数据字典翻译实现(调用 字典翻译接口实现)
│ │ │ ├── DataBaseTranslator.java 数据库翻译服务
│ │ │ ├── DesensitizedTranslator.java 脱敏实现(没啥操作,就返回原值)
│ │ │ ├── EnumTranslator.java 枚举翻译实现
│ │ │ └── TranslatorHandle.java 翻译操作类
│ │ └── Translatable.java 翻译接口(字典、枚举、....接实现该接口)
│ │ │ └── JsonConvertTranslator.java json翻译实现
│ │ ├── Translatable.java 翻译接口(字典、枚举、....接实现该接口)
│ │ └── DictTranslateService.java 字典翻译接口
│ ├── TranslatorBootApplication.java
│ └── util 一些工具类
│ ├── LambdaUtil.java
@ -68,7 +73,7 @@ EasyTranslate
<dependency>
<groupId>com.aizuda</groupId>
<artifactId>dict-trans</artifactId>
<version>${latestVersion}</version>
<version>0.2</version>
</dependency>
<!-- hutool工具类 -->
@ -309,7 +314,13 @@ private String id;
private String zh;
private String zsxm;
// =========================== 示例7 json字符串翻译为json对象 ===========================
@Translate(dictClass = JSONConvert.class, translateField = "jsonObj")
private String json;
private Object jsonObj;
```
#### @Translator

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -11,7 +11,7 @@
<groupId>com.aizuda</groupId>
<artifactId>dict-trans</artifactId>
<packaging>jar</packaging>
<version>0.1</version>
<version>0.2</version>
<modelVersion>4.0.0</modelVersion>
<properties>

View File

@ -15,46 +15,45 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Dictionary {
/**
* 字典表名
* <p>
* 为空时取 {@link TableName#value()}取不到则取 类名驼峰转大写下划线
* 为空时取 {@link TableName#value()}取不到则取 类名驼峰转大写下划线
* </p>
*/
String table() default "";
/**
* 字典编码的属性
* <p>
* 查询条件的列字段需要的是表字段
* 查询条件的列字段需要的是表字段
* </p>
*/
String codeColumn() default "";
/**
* 字典值的列名
* <p>
* 要查询的字段需要的是表字段
* 要查询的字段需要的是表字段
* </p>
*/
String[] textColumn() default {};
/**
* 字典组别属性
* 某些字典可能会需要根据某个类别划分再进行翻译如静态字典中的DICT_ID
* 字典组别属性 某些字典可能会需要根据某个类别划分再进行翻译如静态字典中的DICT_ID
*/
String groupColumn() default "";
/**
* 自定义翻译方法
* <p>
* 1.不配置默认使用DataBaseTranslator翻译
* 2.遇到特殊的翻译场景可自定义翻译实现需要自行编写实现类实现Translatable接口并实现翻译方法
* 程序将使用该方法进行翻译该注解中的所有配置信息将传递到实现方法中
* <ul>
* <li>1.不配置默认使用DataBaseTranslator翻译</li>
* <li>2.遇到特殊的翻译场景可自定义翻译实现需要自行编写实现类实现Translatable接口并实现翻译方法程序将使用该方法进行翻译该注解中的所有配置信息将传递到实现方法中</li>
* </ul>
*
* @see Translatable
*/
Class<? extends Translatable> translator() default Translatable.class;
}

View File

@ -19,9 +19,10 @@ public @interface Translate {
* 字典配置类 指定的class上必须有@Dictionary注解或者是IDictEnum接口的实现类 一般情况下本属性必须指定或者使用别名value属性
*
* <ul>
* <li>DictTranslate 实现类字典翻译</li>
* <li>IEnum 实现类枚举翻译</li>
* <li>Dict字典翻译</li>
* <li>IDict实现类枚举翻译</li>
* <li>Desensitized: 脱敏翻译</li>
* <li>JSONConvert: json字符串翻译</li>
* <li>其他: 数据库翻译</li>
* </ul>
*/

View File

@ -12,10 +12,10 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Translator {
/**
* 指定翻译配置类可传多个一般为实体class
*/
Class<?>[] value() default void.class;
}

View File

@ -1,7 +1,7 @@
package com.aizuda.trans.aspect;
import com.aizuda.trans.annotation.Translator;
import com.aizuda.trans.service.impl.TranslatorHandle;
import com.aizuda.trans.handler.TranslatorHandle;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
@ -22,19 +22,19 @@ import org.springframework.stereotype.Component;
@Aspect
@Component
public class TranslateAspect {
@Pointcut("@annotation(com.aizuda.trans.annotation.Translator)")
public void pointCut() {
}
@AfterReturning(pointcut = "pointCut()", returning = "object")
public void doAfter(JoinPoint joinPoint, Object object) {
if (null == object) {
return;
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Translator config = signature.getMethod().getAnnotation(Translator.class);
Translator config = signature.getMethod().getAnnotation(Translator.class);
TranslatorHandle.parse(object, config.value());
}
}

View File

@ -14,11 +14,11 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
public class TranslatorConfig {
@Bean
@ConditionalOnMissingBean
public DictTranslateService dictTranslateService() {
return new DefaultDictTranslateServiceImpl();
}
}

View File

@ -7,4 +7,5 @@ package com.aizuda.trans.desensitized;
* @date 2022-12-08 008 10:50:37
*/
public class Desensitized implements IDesensitized {
}

View File

@ -7,4 +7,5 @@ package com.aizuda.trans.desensitized;
* @date 2022-12-08 008 10:47:22
*/
public interface IDesensitized {
}

View File

@ -10,17 +10,17 @@ import java.util.concurrent.ConcurrentHashMap;
* @create 2020-04
*/
class EnumPool {
private static final Map<IEnum, EnumBean> DICT_MAP = new ConcurrentHashMap<>();
static void putDict(IEnum dict, String code, String text) {
DICT_MAP.put(dict, new EnumBean(code, text));
}
static EnumBean getDict(IEnum dict) {
return DICT_MAP.get(dict);
}
static class EnumBean implements IEnum {
private String code;
private String text;
@ -29,16 +29,16 @@ class EnumPool {
this.code = code;
this.text = text;
}
@Override
public String getCode() {
return code;
}
@Override
public String getText() {
return text;
}
}
}

View File

@ -52,6 +52,9 @@ public interface IEnum {
/**
* 获取code
*
* @return {@link String }
* @author song_jx
*/
default String getCode() {
return EnumPool.getDict(this).getCode();
@ -59,6 +62,9 @@ public interface IEnum {
/**
* 获取text
*
* @return {@link String }
* @author song_jx
*/
default String getText() {
return EnumPool.getDict(this).getText();

View File

@ -1,4 +1,4 @@
package com.aizuda.trans.service.impl;
package com.aizuda.trans.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
@ -12,8 +12,9 @@ import com.aizuda.trans.desensitized.IDesensitized;
import com.aizuda.trans.dict.DictTranslate;
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.util.LambdaUtil;
import com.aizuda.trans.service.impl.*;
import com.aizuda.trans.util.NameUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -29,15 +30,17 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.aizuda.trans.util.LambdaUtil.uncheck;
/**
* 翻译工具 结合注解使用
*
* @author luozhan
* @see Translate
* @see com.aizuda.trans.annotation.Dictionary
* @see Dictionary
*/
@Slf4j
@Component
public class TranslatorHandle {
/**
@ -94,81 +97,86 @@ public class TranslatorHandle {
return origins;
}
// 源数据中属性的格式大写下划线小写下划线驼峰
final FormatType fieldFormatType = getFieldType(origins);
// 获取数据类型
classes = (classes.length != 0 && classes[0] != void.class) ? classes : new Class[]{origins.get(0).getClass()};
// 获取bo中需要翻译的属性
List<Field> translateFieldList = Arrays.stream(classes)
.map(Class::getDeclaredFields)
// 获取类中需要翻译的属性
final List<Field> translateFieldList = Stream.of(classes)
.map(c -> ReflectUtil.getFields(c))
.flatMap(Stream::of)
.filter(field -> field.isAnnotationPresent(Translate.class))
.collect(Collectors.toList());
// 源数据中属性的格式大写下划线小写下划线驼峰
FormatType fieldFormatType = getFieldType(origins);
for (Field field : translateFieldList) {
// 1.获取要翻译的属性名
String fieldName = NameUtil.parseCamelTo(field.getName(), fieldFormatType);
final String fName = field.getName();
final String fieldName = NameUtil.parseCamelTo(fName, fieldFormatType);
// 2.获取每个待翻译属性的配置
Translate translateConfig = field.getAnnotation(Translate.class);
// 配置的字典class
Class<?> dictClass = getDictClass(translateConfig);
// 获取翻译值写入的字段名
List<String> translateFields = NameUtil.parseCamelTo(
getTranslateFieldName(translateConfig, field.getName()), fieldFormatType);
// 字典组字段值
String groupValue = translateConfig.groupValue();
// 判断条件字段
String conditionField = translateConfig.conditionField();
// 获取敏感词模型
String desensitizedModel = translateConfig.desensitizedModel();
// 字典注解配置
com.aizuda.trans.annotation.Dictionary dictionaryConfig = handle(translateConfig);
final Translate translateConfig = field.getAnnotation(Translate.class);
final Class<?> dictClass = getDictClass(translateConfig);
final List<String> writeFieldList = NameUtil.parseCamelTo(getTranslateFieldName(translateConfig, fName),
fieldFormatType);
final String groupValue = translateConfig.groupValue();
final String conditionField = translateConfig.conditionField();
final String desensitizedModel = translateConfig.desensitizedModel();
final Dictionary dictionaryConfig = handle(dictClass, translateConfig);
final boolean isJsonConvert = IJsonConvert.class.isAssignableFrom(dictClass);
for (T origin : origins) {
// 获取未翻译的原值如果值为空则跳过
String originValue = Convert.toStr(getProperty(origin, fieldName));
if (originValue == null) {
continue;
}
// 获取条件字段值
String conditionFieldValue = null;
if (StrUtil.isNotBlank(conditionField)) {
conditionFieldValue = Convert.toStr(getProperty(origin, conditionField));
}
// 翻译
List<String> translateValues = parse(originValue, dictionaryConfig, dictClass, groupValue, conditionFieldValue);
// 如果敏感词模型不为空则将结果进行敏感词处理
if (StrUtil.isNotBlank(desensitizedModel)) {
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);
final int[] indexArray = StrUtil.splitToInt(model, StrUtil.COMMA);
Assert.isTrue(indexArray.length == 2, "自定义敏感词模型格式:{含开始位置,含结束位置}。举例:{1,2}");
translateValues = translateValues.stream()
.map(s -> StrUtil.hide(s, indexArray[0], indexArray[1]))
.collect(Collectors.toList());
} else {
translateValues = translateValues.stream()
.map(s -> StrUtil.desensitized(s, desensitizedType))
.collect(Collectors.toList());
}
// 获取翻译结果并脱敏处理
List<Object> translateValueList = parse(originValue, dictionaryConfig, dictClass, groupValue, conditionFieldValue);
if (!isJsonConvert) {
// 不是 JsonConvert.class 时可进行脱敏
translateValueList = desensitizedHandle(desensitizedModel, translateValueList);
}
// 填值
setProperty(origin, translateFields, translateValues);
// 将翻译结果填入翻译展示字段
setProperty(origin, isJsonConvert, fieldName, writeFieldList, translateValueList);
}
}
return origins;
}
/**
* 翻译单值
*
* @param originValue 原始值
* @param dictionaryConfig 字典配置
* @param dictClass 字典class包含组别属性字典code属性字典值属性三个信息
* @param groupValue 组别的值由使用者指定
* @param conditionValue 条件字段值
* @return {@link List }<{@link Object }>
* @author song_jx
*/
public static List<Object> parse(String originValue, Dictionary dictionaryConfig, Class<?> dictClass, String groupValue,
String conditionValue) {
if (originValue == null) {
return null;
}
final Translatable translator = getTranslatable(dictClass, dictionaryConfig.translator());
return CollUtil.defaultIfEmpty(translator.translate(groupValue, conditionValue, originValue, dictionaryConfig, dictClass),
Collections.emptyList());
}
/**
* 判断源数据中的属性格式类型
*
* @param origins 对象数组
* @return {@link FormatType }
* @author nn200433
* @author song_jx
*/
private static <T> FormatType getFieldType(List<T> origins) {
T element = origins.get(0);
@ -191,7 +199,7 @@ public class TranslatorHandle {
*
* @param translateConfig 转换配置
* @return {@link Class }<{@link ? }>
* @author nn200433
* @author song_jx
*/
private static Class<?> getDictClass(Translate translateConfig) {
final Class<?> classType = translateConfig.dictClass();
@ -201,15 +209,16 @@ public class TranslatorHandle {
/**
* 将dictClass类上的Dictionary的配置填充到Translate注解中的dictionary属性中 此步骤将合并两个注解中的配置且Translate注解中的配置优先级更高
*
* @param dictClass 字典配置类
* @param translateConfig 转换配置
* @return {@link com.aizuda.trans.annotation.Dictionary }
* @author nn200433
* @return {@link Dictionary }
* @author song_jx
*/
private static com.aizuda.trans.annotation.Dictionary handle(Translate translateConfig) {
Class<?> dictClass = getDictClass(translateConfig);
com.aizuda.trans.annotation.Dictionary dictionaryConfigOnDictClass = dictClass.getAnnotation(com.aizuda.trans.annotation.Dictionary.class);
com.aizuda.trans.annotation.Dictionary dictionaryConfigInTranslateConfig = translateConfig.dictionary();
return (com.aizuda.trans.annotation.Dictionary) joinAnnotationValue(dictionaryConfigOnDictClass, dictionaryConfigInTranslateConfig);
private static Dictionary handle(Class<?> dictClass, Translate translateConfig) {
// Class<?> dictClass = getDictClass(translateConfig);
Dictionary dictionaryConfigOnDictClass = dictClass.getAnnotation(Dictionary.class);
Dictionary dictionaryConfigInTranslateConfig = translateConfig.dictionary();
return (Dictionary) joinAnnotationValue(dictionaryConfigOnDictClass, dictionaryConfigInTranslateConfig);
}
/**
@ -221,22 +230,23 @@ public class TranslatorHandle {
*/
private static Annotation joinAnnotationValue(Annotation annotationFrom, Annotation annotationTo) {
if (annotationTo == null) {
// 这条似乎永远不成立不知道原作者为何这样写先留着吧... dictionary 属性有默认值
return annotationFrom;
}
if (annotationFrom == null) {
return annotationTo;
}
Object handlerFrom = Proxy.getInvocationHandler(annotationFrom);
Object handlerTo = Proxy.getInvocationHandler(annotationTo);
Field fieldFrom = LambdaUtil.uncheck(() -> handlerFrom.getClass().getDeclaredField("memberValues"));
Field fieldTo = LambdaUtil.uncheck(() -> handlerTo.getClass().getDeclaredField("memberValues"));
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 = LambdaUtil.uncheck(() -> (Map) fieldFrom.get(handlerFrom));
Map<String, Object> memberValuesTo = LambdaUtil.uncheck(() -> (Map) fieldTo.get(handlerTo));
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())
@ -256,69 +266,28 @@ public class TranslatorHandle {
return annotationTo;
}
/**
* 翻译单值
*
* @param originValue 原始值
* @param dictConfig 字典配置
* @param dictClass 字典class包含组别属性字典code属性字典值属性三个信息
* @param groupValue 组别的值由使用者指定
* @param conditionValue 条件字段值
* @return 翻译后的值如果字典中找不到翻译值返回原始值
* @author nn200433
*/
public static List<String> parse(String originValue, Dictionary dictConfig, Class<?> dictClass, String groupValue,
String conditionValue) {
if (originValue == null) {
return null;
}
Class<? extends Translatable> translatorClass = dictConfig.translator();
if (translatorClass == Translatable.class) {
if (DictTranslate.class.isAssignableFrom(dictClass)) {
// 1.dictClass是字典类采用字典翻译
translatorClass = DictCacheTranslator.class;
} else if (IEnum.class.isAssignableFrom(dictClass)) {
// 2.dictClass是枚举类采用枚举翻译
translatorClass = EnumTranslator.class;
} else if (IDesensitized.class.isAssignableFrom(dictClass)) {
// 3.dictClass是脱敏类采用脱敏翻译
translatorClass = DesensitizedTranslator.class;
} else {
// 4.否则使用数据库翻译
translatorClass = DataBaseTranslator.class;
}
}
// 调用翻译方法
Translatable translator;
if (translatorClass.isAnnotationPresent(Component.class)) {
// 实现类上配置了@Component则使用Spring容器获取
translator = SpringUtil.getBean(translatorClass);
} else {
translator = LambdaUtil.uncheck(translatorClass::newInstance);
}
List<String> translateResult = translator.translate(groupValue, conditionValue, originValue, dictConfig,
dictClass);
return CollUtil.defaultIfEmpty(translateResult, Collections.emptyList());
}
/**
* 若注解中未配置translateField则默认将原属性名的Id或Code字样替换成Name
* <p>
* resTypeId -> resTypeCode staff -> staffName
* resTypeId / resTypeCode -> staffName
*
* @param translateConfig 转换配置
* @param originFieldName 原始字段名称
* @return {@link List }<{@link String }>
* @author nn200433
* @author song_jx
*/
private static List<String> getTranslateFieldName(Translate translateConfig, String originFieldName) {
String[] translateFieldArray = translateConfig.translateField();
String newName = originFieldName.replaceFirst("(Id|Code)$|$", "Name");
for (int i = 0; i < translateFieldArray.length; i++) {
final String translateField = translateFieldArray[i];
if (StrUtil.isBlank(translateField)) {
translateFieldArray[i] = newName + i;
String[] translateFieldArray = translateConfig.translateField();
final int translateFieldLen = translateFieldArray.length;
final String newName = originFieldName.replaceFirst("(Id|Code)$|$", "Name");
if (translateFieldLen == 0) {
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);
@ -330,44 +299,49 @@ public class TranslatorHandle {
* @param o 对象
* @param fieldName 字段名称
* @return {@link Object }
* @author nn200433
* @author song_jx
*/
private static Object getProperty(Object o, String fieldName) {
if (o instanceof Map) {
return ((Map) o).get(fieldName);
} else {
Method getMethod = getMethod(o.getClass(), fieldName, "get");
// 此处不会抛异常
return LambdaUtil.uncheck(() -> getMethod.invoke(o));
return uncheck(() -> getMethod(o.getClass(), fieldName, "get").invoke(o));
}
}
/**
* 设置属性
*
* @param o 对象
* @param fieldNameList 字段名称列表
* @param valueList 值列表
* @author nn200433
* @param o 对象
* @param isRemoveOriginField 是否删除未翻译的字段
* @param originFieldName 未翻译的字段名
* @param writeFieldList 待写入翻译的字段名称列表
* @param valueList 待写入翻译的值的列表
* @author song_jx
*/
private static void setProperty(Object o, List<String> fieldNameList, List<String> valueList) {
if (fieldNameList.size() > valueList.size()) {
private static void setProperty(Object o, boolean isRemoveOriginField, String originFieldName, List<String> writeFieldList,
List<Object> valueList) {
if (writeFieldList.size() > valueList.size()) {
log.error(
"字典翻译查询结果不够翻译,导致翻译异常。\n ---> 当前翻译数据:{} \n ---> 翻译结果需设置 {} 个字段({}\n ---> 实际翻译出 {} 个字段({}",
JSONUtil.toJsonStr(o), fieldNameList.size(), fieldNameList, valueList.size(), valueList);
JSONUtil.toJsonStr(o), writeFieldList.size(), writeFieldList, valueList.size(), valueList);
return;
}
for (int i = 0; i < fieldNameList.size(); i++) {
final String fieldName = fieldNameList.get(i);
final String value = valueList.get(i);
// 往属性写入数据
for (int i = 0; i < writeFieldList.size(); i++) {
final String writeFieldName = writeFieldList.get(i);
final Object value = valueList.get(i);
if (o instanceof Map) {
((Map) o).put(fieldName, value);
Map rMap = (Map) o;
rMap.put(writeFieldName, value);
if (isRemoveOriginField) {
// 删除字段这辈子都不会触发到的样子...但是下方的对象又没法操作...
rMap.remove(originFieldName);
}
} else {
Method setMethod = getMethod(o.getClass(), fieldName, "set");
// 此处不会抛异常
LambdaUtil.uncheck(() -> setMethod.invoke(o, value));
uncheck(() -> getMethod(o.getClass(), writeFieldName, "set").invoke(o, value));
}
}
}
@ -379,16 +353,83 @@ public class TranslatorHandle {
* @param fieldName 字段名称
* @param prefix 前缀
* @return {@link Method }
* @author nn200433
* @author song_jx
*/
private static Method getMethod(Class<?> clazz, String fieldName, String prefix) {
String methodName = prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return Stream.of(clazz.getDeclaredMethods())
.filter(method -> method.getName().equals(methodName))
.findAny()
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方法"));
}
/**
* 脱敏处理
*
* @param desensitizedModel 脱敏模型
* @param translateValueList 翻译值
* @return {@link List }<{@link Object }>
* @author song_jx
*/
private static List<Object> desensitizedHandle(String desensitizedModel, List<Object> translateValueList) {
// 如果敏感词模型为空则将返回默认翻译值
List<Object> newValueList = translateValueList;
if (StrUtil.isBlank(desensitizedModel)) {
return newValueList;
}
// 否则进行脱敏操作
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);
final int[] indexArray = StrUtil.splitToInt(model, StrUtil.COMMA);
Assert.isTrue(indexArray.length == 2, "自定义敏感词模型格式:{含开始位置,含结束位置}。举例:{1,2}");
newValueList = translateValueList.stream()
.map(s -> StrUtil.hide(Convert.toStr(s), indexArray[0], indexArray[1]))
.collect(Collectors.toList());
} else {
newValueList = translateValueList.stream()
.map(s -> StrUtil.desensitized(Convert.toStr(s), desensitizedType))
.collect(Collectors.toList());
}
return newValueList;
}
/**
* 获取翻译实现
*
* @param dictClass 字典class
* @param translatorClass 实际翻译类
* @return {@link Translatable }
* @author song_jx
*/
private static Translatable getTranslatable(Class<?> dictClass, Class<? extends Translatable> translatorClass) {
// 没这样写dictionary = @Dictionary(translator = UserInfoTranslatorImpl.class)一律认为是默认的 Translatable
// 因为注解定义了 Dictionary dictionary() default @Dictionary;
if (translatorClass == Translatable.class) {
if (DictTranslate.class.isAssignableFrom(dictClass)) {
// 1.dictClass是字典类采用字典翻译
translatorClass = DictCacheTranslator.class;
} else if (IEnum.class.isAssignableFrom(dictClass)) {
// 2.dictClass是枚举类采用枚举翻译
translatorClass = EnumTranslator.class;
} else if (IDesensitized.class.isAssignableFrom(dictClass)) {
// 3.dictClass是脱敏类采用脱敏翻译
translatorClass = DesensitizedTranslator.class;
} else if (IJsonConvert.class.isAssignableFrom(dictClass)) {
// 4.dictClass是JSON类采用JSON翻译
translatorClass = JsonConvertTranslator.class;
} else {
// 5.否则使用数据库翻译
translatorClass = DataBaseTranslator.class;
}
}
// Translatable 实现类上配置了 @Component 则使用 Spring 容器获取 否者 new 一个实现类
return translatorClass.isAnnotationPresent(Component.class) ? SpringUtil.getBean(translatorClass) : uncheck(translatorClass::newInstance);
}
}

View File

@ -0,0 +1,11 @@
package com.aizuda.trans.json;
/**
* JSON接口
*
* @author song_jx
* @date 2022-12-08 008 10:47:22
*/
public interface IJsonConvert {
}

View File

@ -0,0 +1,11 @@
package com.aizuda.trans.json;
/**
* JSON 转换
*
* @author song_jx
* @date 2022-12-08 008 10:50:37
*/
public class JSONConvert implements IJsonConvert {
}

View File

@ -7,15 +7,15 @@ package com.aizuda.trans.service;
* @date 2022-08-18 018 16:31:43
*/
public interface DictTranslateService {
/**
* 获取字典标签
*
* @param dictCode 分组
* @param dictCode 分组
* @param dictValue
* @return {@link String }
* @author nn200433
*/
public String findDictLabel(String dictCode, String dictValue);
}

View File

@ -13,7 +13,7 @@ import java.util.List;
* @create 2020-04
*/
public interface Translatable {
/**
* 自定义翻译方法需要自己实现
*
@ -22,8 +22,10 @@ public interface Translatable {
* @param origin 待翻译的原始值对应字典code属性
* @param dictConfig 字典注解可获取属性配置
* @param dictClass 字典class
* @return 字典value可以返回null值翻译时会处理如果为null则显示原始值
* @return {@link List }<{@link Object }> 字典value可以返回null值翻译时会处理如果为null则显示原始值
* @author nn200433
*/
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass);
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass);
}

View File

@ -42,31 +42,35 @@ public class DataBaseTranslator implements Translatable {
private static DataSource dataSource = SpringUtil.getBean(DataSource.class);
@Override
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass) {
// 获取参数
String codeColumn = dictConfig.codeColumn();
String codeColumn = dictConfig.codeColumn();
String[] textColumnArray = dictConfig.textColumn();
String groupColumn = dictConfig.groupColumn();
String tableName = getTableName(dictConfig, dictClass);
Assert.isTrue(StrUtil.isNotEmpty(codeColumn), "@Dictionary注解codeColumn配置有误找不到指定的属性名class:" + dictClass.getSimpleName());
Assert.isTrue(ArrayUtil.isNotEmpty(textColumnArray), "@Dictionary注解textColumn配置有误找不到指定的属性名class:" + dictClass.getSimpleName());
List<String> rsList = new ArrayList<String>(textColumnArray.length);
String groupColumn = dictConfig.groupColumn();
String tableName = getTableName(dictConfig, dictClass);
Assert.isTrue(StrUtil.isNotEmpty(codeColumn),
"@Dictionary注解codeColumn配置有误找不到指定的属性名class:" + dictClass.getSimpleName());
Assert.isTrue(ArrayUtil.isNotEmpty(textColumnArray),
"@Dictionary注解textColumn配置有误找不到指定的属性名class:" + dictClass.getSimpleName());
List<Object> rsList = new ArrayList<Object>(textColumnArray.length);
try {
log.debug("---> 触发字典翻译:查询表 {} 中的字段 {} ,查询条件 {} = {}", tableName, textColumnArray, codeColumn, origin);
log.debug("---> 触发字典翻译:查询表 {} 中的字段 {} ,查询条件 {} = {}", tableName, textColumnArray, codeColumn,
origin);
// 查询条件为空时直接返回相对应个数的空数组
if (ObjectUtil.isNull(origin) || StrUtil.isBlank(origin)) {
log.debug("---> 触发字典翻译:查询条件为空,直接返回对应个数的空数组");
return Opt.ofNullable(textColumnArray).stream().map(s -> StrUtil.EMPTY).collect(Collectors.toList());
}
Entity where = Entity.create(tableName);
if (StrUtil.isNotEmpty(groupColumn)) {
where.set(groupColumn, groupValue);
}
if (StrUtil.contains(origin, StrUtil.COMMA)) {
// 多条记录取单列查询结果为多条记录循环后转为单条
// 传入数据1,2,3
@ -93,7 +97,7 @@ public class DataBaseTranslator implements Translatable {
} catch (SQLException e) {
log.error("---> DataBaseTranslator 字典翻译SQL查询异常", e);
}
return rsList;
}
@ -109,18 +113,19 @@ public class DataBaseTranslator implements Translatable {
if (StrUtil.isNotEmpty(dictConfig.table())) {
return dictConfig.table();
}
// 类名转表名
final String className = dictClass.getSimpleName();
String tName = className.substring(0, 1) + NameUtil.parseCamelTo(className.substring(1), FormatType.UPPERCASE_UNDERLINE);
String tName = className.substring(0, 1) + NameUtil.parseCamelTo(className.substring(1),
FormatType.UPPERCASE_UNDERLINE);
// 获取mp注解上的表名
TableName tableName = (TableName) dictClass.getAnnotation(TableName.class);
if (null != tableName) {
tName = tableName.value();
}
return tName;
}
}

View File

@ -9,10 +9,10 @@ import com.aizuda.trans.service.DictTranslateService;
* @date 2022-08-18 018 16:32:24
*/
public class DefaultDictTranslateServiceImpl implements DictTranslateService {
@Override
public String findDictLabel(String dictCode, String dictValue) {
return dictValue;
}
}

View File

@ -17,11 +17,12 @@ import java.util.List;
@Slf4j
@Component
public class DesensitizedTranslator implements Translatable {
@Override
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass) {
// 直接返回原值
return Collections.singletonList(origin);
}
}

View File

@ -19,14 +19,15 @@ import java.util.List;
@Slf4j
@Component
public class DictCacheTranslator implements Translatable {
@Autowired
private DictTranslateService dictTranslateService;
@Override
@SuppressWarnings("unchecked")
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass) {
return Collections.singletonList(dictTranslateService.findDictLabel(groupValue, origin));
}
}

View File

@ -18,16 +18,18 @@ import java.util.stream.Stream;
*/
@Component
public class EnumTranslator implements Translatable {
@Override
@SuppressWarnings("unchecked")
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig, Class dictClass) {
Assert.isTrue(IEnum.class.isAssignableFrom(dictClass), dictClass.getSimpleName() + "不是IDictEnum的实现类无法使用EnumTranslator进行翻译");
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass) {
Assert.isTrue(IEnum.class.isAssignableFrom(dictClass),
dictClass.getSimpleName() + "不是IDictEnum的实现类无法使用EnumTranslator进行翻译");
final String s = Stream.of(((Class<IEnum>) dictClass).getEnumConstants())
.filter((IEnum e) -> e.getCode().equals(origin))
.map(IEnum::getText)
.findAny().orElse(null);
return Collections.singletonList(s);
}
}

View File

@ -0,0 +1,36 @@
package com.aizuda.trans.service.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aizuda.trans.annotation.Dictionary;
import com.aizuda.trans.service.Translatable;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
/**
* JSON 翻译
*
* @author luozhan
* @create 2020-04
*/
@Component
public class JsonConvertTranslator implements Translatable {
@Override
@SuppressWarnings("unchecked")
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass) {
Assert.isTrue(JSONUtil.isTypeJSON(origin), "该数据不是 JSON 字符串JSON 字符串应为 “{}” 或 “[]” 包裹的数据。");
Object rObj = null;
if (JSONUtil.isTypeJSONObject(origin)) {
rObj = JSONUtil.toBean(origin, JSONObject.class);
} else {
rObj = JSONUtil.toList(origin, JSONObject.class);
}
return Collections.singletonList(rObj);
}
}

View File

@ -10,7 +10,7 @@ import java.util.function.*;
* @link <a>https://github.com/Robot-L/LambdaExceptionUtil</a>觉得不错的话请给个Starthx! :D
*/
public final class LambdaUtil {
/**
* 包装普通函数Function
* <p>
@ -18,7 +18,8 @@ public final class LambdaUtil {
* 1.外层代码中编译器将无法提示有异常需要处理
* 2.也无法主动在外层捕获具体的异常如果尝试try一个具体的异常编译器将提示在try语句体中永远不会抛出相应异常Exception 'XXX' is never thrown in the corresponding try block
*/
public static <T, R, E extends Exception> Function<T, R> wrapFunction(FunctionWithExceptions<T, R, E> function) throws E {
public static <T, R, E extends Exception> Function<T, R> wrapFunction(FunctionWithExceptions<T, R, E> function)
throws E {
return t -> {
try {
return function.apply(t);
@ -28,11 +29,13 @@ public final class LambdaUtil {
}
};
}
/**
* 包装双入参普通函数BiFunction
*/
public static <T, U, R, E extends Exception> BiFunction<T, U, R> wrapBiFunction(BiFunctionWithExceptions<T, U, R, E> biFunction) throws E {
public static <T, U, R, E extends Exception> BiFunction<T, U, R> wrapBiFunction(
BiFunctionWithExceptions<T, U, R, E> biFunction)
throws E {
return (t, u) -> {
try {
return biFunction.apply(t, u);
@ -42,11 +45,12 @@ public final class LambdaUtil {
}
};
}
/**
* 包装消费函数Consumer
*/
public static <T, E extends Exception> Consumer<T> wrapConsumer(ConsumerWithExceptions<T, E> consumer) throws E {
public static <T, E extends Exception> Consumer<T> wrapConsumer(ConsumerWithExceptions<T, E> consumer)
throws E {
return t -> {
try {
consumer.accept(t);
@ -55,11 +59,12 @@ public final class LambdaUtil {
}
};
}
/**
* 包装双重消费函数BiConsumer
*/
public static <T, U, E extends Exception> BiConsumer<T, U> wrapBiConsumer(BiConsumerWithExceptions<T, U, E> biConsumer) throws E {
public static <T, U, E extends Exception> BiConsumer<T, U> wrapBiConsumer(BiConsumerWithExceptions<T, U, E> biConsumer)
throws E {
return (t, u) -> {
try {
biConsumer.accept(t, u);
@ -68,11 +73,12 @@ public final class LambdaUtil {
}
};
}
/**
* 包装生产函数Supplier
*/
public static <T, E extends Exception> Supplier<T> wrapSupplier(SupplierWithExceptions<T, E> function) throws E {
public static <T, E extends Exception> Supplier<T> wrapSupplier(SupplierWithExceptions<T, E> function)
throws E {
return () -> {
try {
return function.get();
@ -82,11 +88,12 @@ public final class LambdaUtil {
}
};
}
/**
* 包装条件函数Predicate
*/
public static <T, E extends Exception> Predicate<T> wrapPredicate(PredicateWithExceptions<T, E> predicate) throws E {
public static <T, E extends Exception> Predicate<T> wrapPredicate(PredicateWithExceptions<T, E> predicate)
throws E {
return t -> {
try {
return predicate.test(t);
@ -96,11 +103,12 @@ public final class LambdaUtil {
}
};
}
/**
* 包装双入参条件函数BiPredicate
*/
public static <T, U, E extends Exception> BiPredicate<T, U> wrapBiPredicate(BiPredicateWithExceptions<T, U, E> predicate) throws E {
public static <T, U, E extends Exception> BiPredicate<T, U> wrapBiPredicate(BiPredicateWithExceptions<T, U, E> predicate)
throws E {
return (t, u) -> {
try {
return predicate.test(t, u);
@ -110,18 +118,19 @@ public final class LambdaUtil {
}
};
}
/**
* 包装纯执行函数Runnable
*/
public static <E extends Exception> void wrapRunnable(RunnableWithExceptions<E> runnable) throws E {
public static <E extends Exception> void wrapRunnable(RunnableWithExceptions<E> runnable)
throws E {
try {
runnable.run();
} catch (Exception exception) {
throwAsUnchecked(exception);
}
}
/**
* 如果一个方法绝对不会抛出所申明的异常可以使用该方法进行包装
* new String(byteArr, "UTF-8")申明了UnsupportedEncodingException
@ -136,55 +145,64 @@ public final class LambdaUtil {
return null;
}
}
@SuppressWarnings("unchecked")
public static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
public static <E extends Throwable> void throwAsUnchecked(Exception exception)
throws E {
throw (E) exception;
}
@FunctionalInterface
public interface ConsumerWithExceptions<T, E extends Exception> {
void accept(T t) throws E;
void accept(T t)
throws E;
}
@FunctionalInterface
public interface BiConsumerWithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
void accept(T t, U u)
throws E;
}
@FunctionalInterface
public interface FunctionWithExceptions<T, R, E extends Exception> {
static <T> FunctionWithExceptions<T, T, Exception> identity() {
return t -> t;
}
R apply(T t) throws E;
R apply(T t)
throws E;
}
@FunctionalInterface
public interface BiFunctionWithExceptions<T, U, R, E extends Exception> {
R apply(T t, U u) throws E;
R apply(T t, U u)
throws E;
}
@FunctionalInterface
public interface SupplierWithExceptions<T, E extends Exception> {
T get() throws E;
T get()
throws E;
}
@FunctionalInterface
public interface RunnableWithExceptions<E extends Exception> {
void run() throws E;
void run()
throws E;
}
@FunctionalInterface
public interface PredicateWithExceptions<T, E extends Exception> {
boolean test(T t) throws E;
boolean test(T t)
throws E;
}
@FunctionalInterface
public interface BiPredicateWithExceptions<T, U, E extends Exception> {
boolean test(T t, U u) throws E;
boolean test(T t, U u)
throws E;
}
}

View File

@ -12,21 +12,21 @@ import java.util.List;
* @date 2023-04-17 04:30:20
*/
public class NameUtil {
/**
* 解析驼峰
*
* @param param 参数
* @param type 类型
* @return {@link String }
* @author nn200433
* @author song_jx
*/
public static String parseCamelTo(String param, FormatType type) {
if (type == FormatType.CAMEL) {
return param;
}
int len = param.length();
StringBuilder sb = new StringBuilder(len);
int len = param.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = param.charAt(i);
if (Character.isUpperCase(c)) {
@ -38,7 +38,7 @@ public class NameUtil {
}
return sb.toString();
}
/**
* 解析驼峰
*
@ -53,5 +53,5 @@ public class NameUtil {
}
return params;
}
}

View File

@ -5,6 +5,7 @@ import com.aizuda.trans.demo.DemoService;
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 org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@ -29,19 +30,25 @@ public class TranslatorTest {
@Test
public void demo1() {
List<People> peopleList = demoService.dictDemo();
Console.log("---> 翻译结果:{}", peopleList);
Console.log("---> 字典 & 脱敏 翻译结果:{}", peopleList);
}
@Test
public void demo2() {
List<Device> deviceList = demoService.enumDemo();
Console.log("---> 翻译结果:{}", deviceList);
Console.log("---> 枚举 翻译结果:{}", deviceList);
}
@Test
public void demo3() {
List<People2> peopleList = demoService.dbDemo();
Console.log("---> 翻译结果:{}", peopleList);
Console.log("---> 数据库 翻译结果:{}", peopleList);
}
@Test
public void demo4() {
List<People3> peopleList = demoService.jsonDemo();
Console.log("---> json 翻译结果:{}", peopleList);
}
}

View File

@ -3,6 +3,7 @@ package com.aizuda.trans.demo;
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 java.util.List;
@ -38,4 +39,12 @@ public interface DemoService {
*/
public List<People2> dbDemo();
/**
* json演示
*
* @return {@link List }<{@link People3 }>
* @author song_jx
*/
public List<People3> jsonDemo();
}

View File

@ -18,9 +18,9 @@ import java.util.List;
public class CustomerTranslateServiceImpl implements Translatable {
@Override
public List<String> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
public List<Object> translate(String groupValue, String conditionValue, String origin, Dictionary dictConfig,
Class dictClass) {
List<String> rList = new ArrayList<String>(1);
List<Object> rList = new ArrayList<Object>(1);
if (StrUtil.equals(origin, "1")) {
rList.add("结果1");
} else {

View File

@ -6,6 +6,7 @@ import com.aizuda.trans.demo.DemoService;
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 org.springframework.stereotype.Component;
import java.util.List;
@ -43,4 +44,12 @@ public class DemoServiceImpl implements DemoService {
return CollUtil.newArrayList(man, woman);
}
@Translator
@Override
public List<People3> jsonDemo() {
People3 man = People3.builder().id("1").json("{\"abc\":\"def\", \"eg\":3}").build();
People3 woman = People3.builder().id("2").json("[{\"a\":\"b\",\"c\":6},{\"d\":\"f\",\"e\":{\"a\":\"6\"}}]").build();
return CollUtil.newArrayList(man, woman);
}
}

View File

@ -0,0 +1,33 @@
package com.aizuda.trans.entity;
import com.aizuda.trans.annotation.Translate;
import com.aizuda.trans.json.JSONConvert;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 人3
*
* @author nn200433
* @date 2022-12-16 016 11:40:30
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class People3 {
/** 数据库翻译 */
@Translate(dictClass = UserDB.class, translateField = "name")
private String id;
private String name;
@Translate(dictClass = JSONConvert.class, translateField = "jsonObj")
private String json;
private Object jsonObj;
}