From f74779d0ad5b9c67a048e4353d065fbae219c63b Mon Sep 17 00:00:00 2001 From: Leo <98382335+gaoziman@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:37:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(plugin):=20=E6=96=B0=E5=A2=9E=E5=AD=97?= =?UTF-8?q?=E5=85=B8=E7=BF=BB=E8=AF=91=E6=8F=92=E4=BB=B6=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增coder-common-thin-dict插件模块 - 实现CoderDictAspect AOP切面自动字典翻译 - 支持@CoderDict和@CoderDictClass注解 - 自动翻译List、Page和单对象返回结果 - 集成Redis缓存提升翻译性能 - 提供@EnableCoderDict启用注解 --- .../coder-common-thin-dict/pom.xml | 35 +++ .../thin/dict/anno/EnableCoderDict.java | 16 ++ .../thin/dict/aspect/CoderDictAspect.java | 212 ++++++++++++++++++ .../thin/dict/aspect/CoderDictModel.java | 21 ++ 4 files changed, 284 insertions(+) create mode 100644 coder-common-thin-plugins/coder-common-thin-dict/pom.xml create mode 100644 coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/anno/EnableCoderDict.java create mode 100644 coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictAspect.java create mode 100644 coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictModel.java diff --git a/coder-common-thin-plugins/coder-common-thin-dict/pom.xml b/coder-common-thin-plugins/coder-common-thin-dict/pom.xml new file mode 100644 index 0000000..f30e5d8 --- /dev/null +++ b/coder-common-thin-plugins/coder-common-thin-dict/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.leocoder.thin + coder-common-thin-plugins + ${revision} + + + coder-common-thin-dict + coder-common-thin-dict + 字典翻译插件 + + + + + org.leocoder.thin + coder-common-thin-common + ${revision} + + + + org.leocoder.thin + coder-common-thin-model + ${revision} + + + + org.springframework.boot + spring-boot-starter-aop + + + \ No newline at end of file diff --git a/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/anno/EnableCoderDict.java b/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/anno/EnableCoderDict.java new file mode 100644 index 0000000..1567534 --- /dev/null +++ b/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/anno/EnableCoderDict.java @@ -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 { + +} \ No newline at end of file diff --git a/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictAspect.java b/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictAspect.java new file mode 100644 index 0000000..b8cf5b5 --- /dev/null +++ b/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictAspect.java @@ -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 dictMap = new HashMap<>(); + + @Resource + private RedisUtil redisUtil; + + @Pointcut("@annotation(org.leocoder.thin.common.anno.CoderDictClass)") + public void logPointCut() { + } + + /** + * @description [翻译数据] + * 需要处理的数据类型: + * 1、分页数据R> + * 2、普通列表R> + * 3、普通数据R + * @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 list = (List) 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 dictModelList = getCoderDict(objectData.getClass()); + // dictModelList.forEach(System.out::println); + // CoderDictModel(dictKey=sys_user_sex, dictValue=sex) + // 如果没有字典映射,则直接返回原始结果 + if (dictModelList.isEmpty()) { + return; + } + + // 获取所有的字典数据 + List 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 listDictCache(List dictModelList) { + List dictDataList = new ArrayList<>(); + List dictKeyList = getDictKey(dictModelList); + for (String dictKey : dictKeyList) { + Boolean hasKey = redisUtil.hasKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey); + if(hasKey) { + List 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 dictModelList, Map 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 getDictKey(List CoderDictBos) { + // 创建一个字符串列表用于存储dictKey + List 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 getCoderDict(Class classData) { + Field[] fields = classData.getDeclaredFields(); // 获取类classData中声明的所有字段 + List 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; + } + +} \ No newline at end of file diff --git a/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictModel.java b/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictModel.java new file mode 100644 index 0000000..b76e63c --- /dev/null +++ b/coder-common-thin-plugins/coder-common-thin-dict/src/main/java/org/leocoder/thin/dict/aspect/CoderDictModel.java @@ -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; +} \ No newline at end of file