feat(plugin): 新增字典翻译插件模块
- 新增coder-common-thin-dict插件模块 - 实现CoderDictAspect AOP切面自动字典翻译 - 支持@CoderDict和@CoderDictClass注解 - 自动翻译List、Page和单对象返回结果 - 集成Redis缓存提升翻译性能 - 提供@EnableCoderDict启用注解
This commit is contained in:
parent
1dc2300763
commit
f74779d0ad
35
coder-common-thin-plugins/coder-common-thin-dict/pom.xml
Normal file
35
coder-common-thin-plugins/coder-common-thin-dict/pom.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.leocoder.thin</groupId>
|
||||
<artifactId>coder-common-thin-plugins</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<name>coder-common-thin-dict</name>
|
||||
<artifactId>coder-common-thin-dict</artifactId>
|
||||
<description>字典翻译插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 通用公共模块 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.thin</groupId>
|
||||
<artifactId>coder-common-thin-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 数据模型模块 -->
|
||||
<dependency>
|
||||
<groupId>org.leocoder.thin</groupId>
|
||||
<artifactId>coder-common-thin-model</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- Aop依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,16 @@
|
||||
package org.leocoder.thin.dict.anno;
|
||||
|
||||
import org.leocoder.thin.common.utils.cache.RedisUtil;
|
||||
import org.leocoder.thin.dict.aspect.CoderDictAspect;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({CoderDictAspect.class, RedisUtil.class})
|
||||
public @interface EnableCoderDict {
|
||||
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
package org.leocoder.thin.dict.aspect;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.leocoder.thin.common.anno.CoderDict;
|
||||
import org.leocoder.thin.common.anno.CoderDictClass;
|
||||
import org.leocoder.thin.common.constants.CoderCacheConstants;
|
||||
import org.leocoder.thin.common.utils.cache.RedisUtil;
|
||||
import org.leocoder.thin.domain.pojo.system.SysDictData;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [CoderDictAspect]
|
||||
*/
|
||||
@Aspect
|
||||
@Order(4)
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CoderDictAspect {
|
||||
|
||||
private Map<String, String> dictMap = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Pointcut("@annotation(org.leocoder.thin.common.anno.CoderDictClass)")
|
||||
public void logPointCut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [翻译数据]
|
||||
* 需要处理的数据类型:
|
||||
* 1、分页数据R<IPage<T>>
|
||||
* 2、普通列表R<List<T>>
|
||||
* 3、普通数据R<T>
|
||||
* @author Leocoder
|
||||
*/
|
||||
@Around("@annotation(CoderDictClass)")
|
||||
public Object CoderTypeDictTranslation(final ProceedingJoinPoint proceedingJoinPoint, CoderDictClass CoderDictClass) throws Throwable {
|
||||
Object proceed = proceedingJoinPoint.proceed();
|
||||
if (ObjectUtils.isEmpty(proceed)) {
|
||||
return proceed;
|
||||
}
|
||||
if (proceed instanceof List) {
|
||||
// 数据字典翻译
|
||||
List<Object> list = (List<Object>) proceed;
|
||||
CoderDictTranslate(list);
|
||||
return list;
|
||||
} else if (proceed instanceof Page) {
|
||||
// Page 类型处理逻辑
|
||||
IPage page = (IPage) proceed;
|
||||
List records = page.getRecords();
|
||||
// 数据字典翻译
|
||||
CoderDictTranslate(records);
|
||||
page.setRecords(records);
|
||||
// 返回修改后的分页对象
|
||||
return page;
|
||||
} else {
|
||||
// 其他类型处理逻辑或错误处理
|
||||
return proceed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description [获取不同类型返回结果数据进行翻译]
|
||||
* @author Leocoder
|
||||
*/
|
||||
private void CoderDictTranslate(Object resultData) {
|
||||
Object objectData;
|
||||
// 检查输入的result是否是List或者ArrayList的实例
|
||||
if (resultData instanceof List) {
|
||||
// 如果是列表,则获取第一个元素
|
||||
List CoderList = ((List) resultData);
|
||||
// 如果列表为空,则直接返回原始结果
|
||||
if (CoderList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 获取集合的第一条数据
|
||||
objectData = CoderList.get(0);
|
||||
} else {
|
||||
// 否则直接使用result对象
|
||||
objectData = resultData;
|
||||
}
|
||||
// 获取数据字典key 和 实体类字段名称
|
||||
List<CoderDictModel> dictModelList = getCoderDict(objectData.getClass());
|
||||
// dictModelList.forEach(System.out::println);
|
||||
// CoderDictModel(dictKey=sys_user_sex, dictValue=sex)
|
||||
// 如果没有字典映射,则直接返回原始结果
|
||||
if (dictModelList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取所有的字典数据
|
||||
List<SysDictData> dictDataList = listDictCache(dictModelList);
|
||||
|
||||
// 如果字典数据是空则直接返回
|
||||
if (CollectionUtils.isEmpty(dictDataList)) {
|
||||
return;
|
||||
}
|
||||
// 将字典值转换成map形式
|
||||
for (SysDictData dictData : dictDataList) {
|
||||
dictMap.put(dictData.getDictType() + "_" + dictData.getDictValue(), dictData.getDictLabel());
|
||||
}
|
||||
// 根据对象类型,为返回数据的每一个对象赋予字典值
|
||||
if (resultData instanceof List) {
|
||||
for (Object entity : (List) resultData) {
|
||||
assignDictValue(entity, dictModelList, dictMap);
|
||||
}
|
||||
} else {
|
||||
assignDictValue(resultData, dictModelList, dictMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description [获取字典数据缓存]
|
||||
* @author Leocoder
|
||||
*/
|
||||
private List<SysDictData> listDictCache(List<CoderDictModel> dictModelList) {
|
||||
List<SysDictData> dictDataList = new ArrayList<>();
|
||||
List<String> dictKeyList = getDictKey(dictModelList);
|
||||
for (String dictKey : dictKeyList) {
|
||||
Boolean hasKey = redisUtil.hasKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey);
|
||||
if(hasKey) {
|
||||
List<SysDictData> dictDataKeyList = redisUtil.getKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey);
|
||||
dictDataList.addAll(dictDataKeyList);
|
||||
}
|
||||
}
|
||||
return dictDataList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param entity 返回List集合数据循环出来的实体类对象,转换字典的核心代码
|
||||
* @param dictModelList CoderDictModel(dictKey=sys_user_sex, dictValue=sex) 获取实体类的@CoderDict注解值
|
||||
* @param dictMap 所有字典数据缓存
|
||||
*/
|
||||
public void assignDictValue(Object entity, List<CoderDictModel> dictModelList, Map<String, String> dictMap) {
|
||||
try {
|
||||
// 遍历每个CoderDictModel对象
|
||||
for (CoderDictModel dictModel : dictModelList) {
|
||||
String dictKey = dictModel.getDictKey(); // 获取字典类型
|
||||
String fieldName = dictModel.getDictValue(); // 获取需要赋值的字段名
|
||||
Class<?> classData = entity.getClass(); // 获取实体类的类
|
||||
Field fieldData = classData.getDeclaredField(fieldName); // 获取需要赋值的字段
|
||||
fieldData.setAccessible(true); // 设置字段可访问性
|
||||
Object originalValue = fieldData.get(entity); // 获取字段的原始值
|
||||
if (ObjectUtils.isNotEmpty(originalValue)) {
|
||||
// 如果原始值不为空,则将字典值赋给字段
|
||||
String dictValue = dictMap.getOrDefault(dictKey + "_" + originalValue, originalValue.toString());
|
||||
fieldData.set(entity, dictValue);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体中所有需要翻译的dictKey,返回需要翻译的dictKey列表
|
||||
*/
|
||||
private List<String> getDictKey(List<CoderDictModel> CoderDictBos) {
|
||||
// 创建一个字符串列表用于存储dictKey
|
||||
List<String> dictKeyList = new ArrayList<>();
|
||||
// 如果传入的CoderDictBo为空,则直接返回空列表
|
||||
if (CollectionUtils.isEmpty(CoderDictBos)) {
|
||||
return dictKeyList;
|
||||
}
|
||||
for (CoderDictModel dictBo : CoderDictBos) {
|
||||
// 将每个CoderDictBo对象的key添加到列表中
|
||||
dictKeyList.add(dictBo.getDictKey());
|
||||
}
|
||||
return dictKeyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体类中配置的dictKey 和 对应的字段名称
|
||||
*/
|
||||
private List<CoderDictModel> getCoderDict(Class classData) {
|
||||
Field[] fields = classData.getDeclaredFields(); // 获取类classData中声明的所有字段
|
||||
List<CoderDictModel> list = new ArrayList<>(); // 创建一个CoderDictBo列表
|
||||
CoderDictModel CoderDictBo;
|
||||
CoderDict CoderDict;
|
||||
for (Field field : fields) {
|
||||
if (field.isAnnotationPresent(CoderDict.class)) { // 检查字段是否带有CoderDict注解
|
||||
CoderDict = field.getAnnotation(CoderDict.class); // 获取CoderDict注解
|
||||
CoderDictBo = new CoderDictModel(CoderDict.dictKey(), field.getName()); // 创建CoderDictBo对象,存储dictKey和字段名
|
||||
list.add(CoderDictBo);
|
||||
}
|
||||
}
|
||||
// 返回包含配置的dictKey和对应字段内容的列表
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package org.leocoder.thin.dict.aspect;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Leocoder
|
||||
* @description [CoderDictModel-@CoderDict注解字段]
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class CoderDictModel {
|
||||
|
||||
/** 字典Key值 */
|
||||
private String dictKey;
|
||||
|
||||
/** 实体类字段名称 */
|
||||
private String dictValue;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user