diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java index 8c9c7f6ca..7f7318487 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java @@ -19,7 +19,7 @@ public class CacheUtils { // 只阻塞当前数据加载线程,其他线程返回旧值 .refreshAfterWrite(duration) // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 - .build(CacheLoader.asyncReloading(loader, Executors.newSingleThreadExecutor())); + .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置 } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml index b1ee580f4..f99d727ba 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml @@ -26,5 +26,25 @@ org.springframework.boot spring-boot-starter + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + true + + + + + cn.iocoder.cloud + yudao-module-system-api + ${revision} + + + + + com.google.guava + guava + diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java index 1828585c0..01f7aba78 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.dict.config; -import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService; import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,8 +10,8 @@ public class YudaoDictAutoConfiguration { @Bean @SuppressWarnings("InstantiationOfUtilityClass") - public DictFrameworkUtils dictUtils(DictDataFrameworkService service) { - DictFrameworkUtils.init(service); + public DictFrameworkUtils dictUtils(DictDataApi dictDataApi) { + DictFrameworkUtils.init(dictDataApi); return new DictFrameworkUtils(); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictRpcAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictRpcAutoConfiguration.java new file mode 100644 index 000000000..83602e818 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictRpcAutoConfiguration.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.framework.dict.config; + +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +/** + * 字典用到 Feign 的配置项 + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +@EnableFeignClients(clients = DictDataApi.class) // 主要是引入相关的 API 服务 +public class YudaoDictRpcAutoConfiguration { +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/dto/DictDataRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/dto/DictDataRespDTO.java deleted file mode 100644 index 150ee751e..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/dto/DictDataRespDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.framework.dict.core.dto; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import lombok.Data; - -/** - * 字典数据 Response DTO - * - * @author 芋道源码 - */ -@Data -public class DictDataRespDTO { - - /** - * 字典标签 - */ - private String label; - /** - * 字典值 - */ - private String value; - /** - * 字典类型 - */ - private String dictType; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/package-info.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/package-info.java new file mode 100644 index 000000000..7ba720a9d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.framework.dict.core; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/service/DictDataFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/service/DictDataFrameworkService.java deleted file mode 100644 index 870f63a55..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/service/DictDataFrameworkService.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.framework.dict.core.service; - -import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO; - -import java.util.List; - -public interface DictDataFrameworkService { - - /** - * 获得指定的字典数据,从缓存中 - * - * @param type 字典类型 - * @param value 字典数据值 - * @return 字典数据 - */ - DictDataRespDTO getDictDataFromCache(String type, String value); - - /** - * 解析获得指定的字典数据,从缓存中 - * - * @param type 字典类型 - * @param label 字典数据标签 - * @return 字典数据 - */ - DictDataRespDTO parseDictDataFromCache(String type, String label); - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java index 976f81c79..0ea5a7608 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java @@ -1,28 +1,75 @@ package cn.iocoder.yudao.framework.dict.core.util; -import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO; -import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.cache.CacheUtils; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import java.time.Duration; + /** * 字典工具类 + * + * @author 芋道源码 */ @Slf4j public class DictFrameworkUtils { - private static DictDataFrameworkService service; + private static DictDataApi dictDataApi; - public static void init(DictDataFrameworkService service) { - DictFrameworkUtils.service = service; + private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO(); + + /** + * 针对 {@link #getDictDataLabel(String, String)} 的缓存 + */ + private static final LoadingCache, DictDataRespDTO> getDictDataCache = CacheUtils.buildAsyncReloadingCache( + Duration.ofMinutes(1L), // 过期时间 1 分钟 + new CacheLoader, DictDataRespDTO>() { + + @Override + public DictDataRespDTO load(KeyValue key) { + CommonResult getDictDataResult = dictDataApi.getDictData(key.getKey(), key.getValue()); + getDictDataResult.checkError(); + return ObjectUtil.defaultIfNull(getDictDataResult.getData(), DICT_DATA_NULL); + } + + }); + + /** + * 针对 {@link #parseDictDataValue(String, String)} 的缓存 + */ + private static final LoadingCache, DictDataRespDTO> parseDictDataCache = CacheUtils.buildAsyncReloadingCache( + Duration.ofMinutes(1L), // 过期时间 1 分钟 + new CacheLoader, DictDataRespDTO>() { + + @Override + public DictDataRespDTO load(KeyValue key) { + CommonResult parseDictDataResult = dictDataApi.parseDictData(key.getKey(), key.getValue()); + parseDictDataResult.checkError(); + return ObjectUtil.defaultIfNull(parseDictDataResult.getData(), DICT_DATA_NULL); + } + + }); + + public static void init(DictDataApi dictDataApi) { + DictFrameworkUtils.dictDataApi = dictDataApi; log.info("[init][初始化 DictFrameworkUtils 成功]"); } - public static DictDataRespDTO getDictDataFromCache(String type, String value) { - return service.getDictDataFromCache(type, value); + @SneakyThrows + public static String getDictDataLabel(String dictType, String value) { + return getDictDataCache.get(new KeyValue<>(dictType, value)).getLabel(); } - public static DictDataRespDTO parseDictDataFromCache(String type, String label) { - return service.parseDictDataFromCache(type, label); + @SneakyThrows + public static String parseDictDataValue(String dictType, String label) { + return parseDictDataCache.get(new KeyValue<>(dictType, label)).getValue(); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/resources/META-INF/spring.factories index 3986b1c2b..39cfcffe0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/resources/META-INF/spring.factories +++ b/yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + cn.iocoder.yudao.framework.dict.config.YudaoDictRpcAutoConfiguration,\ cn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java index beed26f94..7698cf4a6 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.framework.excel.core.convert; import cn.hutool.core.convert.Convert; -import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO; import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import com.alibaba.excel.converters.Converter; @@ -12,7 +11,7 @@ import com.alibaba.excel.metadata.property.ExcelContentProperty; import lombok.extern.slf4j.Slf4j; /** - * Excel {@link DictDataRespDTO} 数据字典转换器 + * Excel 数据字典转换器 * * @author 芋道源码 */ @@ -35,14 +34,14 @@ public class DictConvert implements Converter { // 使用字典解析 String type = getType(contentProperty); String label = cellData.getStringValue(); - DictDataRespDTO dictData = DictFrameworkUtils.parseDictDataFromCache(type, label); - if (dictData == null) { + String value = DictFrameworkUtils.parseDictDataValue(type, label); + if (value == null) { log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label); return null; } // 将 String 的 value 转换成对应的属性 Class fieldClazz = contentProperty.getField().getType(); - return Convert.convert(fieldClazz, dictData.getValue()); + return Convert.convert(fieldClazz, value); } @Override @@ -56,13 +55,13 @@ public class DictConvert implements Converter { // 使用字典格式化 String type = getType(contentProperty); String value = String.valueOf(object); - DictDataRespDTO dictData = DictFrameworkUtils.getDictDataFromCache(type, value); - if (dictData == null) { + String label = DictFrameworkUtils.getDictDataLabel(type, value); + if (label == null) { log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value); return new CellData<>(""); } // 生成 Excel 小表格 - return new CellData<>(dictData.getLabel()); + return new CellData<>(label); } private static String getType(ExcelContentProperty contentProperty) { diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java index 4455da3af..13834f926 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.api.dict; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.enums.ApiConstants; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -27,4 +28,22 @@ public interface DictDataApi { CommonResult validDictDatas(@RequestParam("dictType") String dictType, @RequestParam("values") Collection values); + @GetMapping(PREFIX + "/get") + @ApiOperation("获得指定的字典数据") + @ApiImplicitParams({ + @ApiImplicitParam(name = "dictType", value = "字典类型", example = "SEX", required = true, dataTypeClass = String.class), + @ApiImplicitParam(name = "value", value = "字典数据值", example = "1", required = true, dataTypeClass = String.class) + }) + CommonResult getDictData(@RequestParam("dictType") String dictType, + @RequestParam("value") String value); + + @GetMapping(PREFIX + "/parse") + @ApiOperation("解析获得指定的字典数据") + @ApiImplicitParams({ + @ApiImplicitParam(name = "dictType", value = "字典类型", example = "SEX", required = true, dataTypeClass = String.class), + @ApiImplicitParam(name = "label", value = "字典标签", example = "男", required = true, dataTypeClass = String.class) + }) + CommonResult parseDictData(@RequestParam("dictType") String dictType, + @RequestParam("label") String label); + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/dto/DictDataRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/dto/DictDataRespDTO.java new file mode 100644 index 000000000..c988ea653 --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/dto/DictDataRespDTO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.api.dict.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("RPC 服务 - 字典数据 Response DTO") +@Data +public class DictDataRespDTO { + + @ApiModelProperty(value = "字典标签", required = true, example = "芋道") + private String label; + @ApiModelProperty(value = "字典值", required = true, example = "iocoder") + private String value; + @ApiModelProperty(value = "字典类型", required = true, example = "sys_common_sex") + private String dictType; + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 CommonStatusEnum 枚举") + private Integer status; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java index 589b312f1..c498f3815 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.system.api.dict; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; +import cn.iocoder.yudao.module.system.convert.dict.DictDataConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.module.system.service.dict.DictDataService; import org.apache.dubbo.config.annotation.DubboService; import org.springframework.stereotype.Service; @@ -27,4 +30,16 @@ public class DictDataApiImpl implements DictDataApi { return success(true); } + @Override + public CommonResult getDictData(String dictType, String value) { + DictDataDO dictData = dictDataService.getDictData(dictType, value); + return success(DictDataConvert.INSTANCE.convert02(dictData)); + } + + @Override + public CommonResult parseDictData(String dictType, String label) { + DictDataDO dictData = dictDataService.parseDictData(dictType, label); + return success(DictDataConvert.INSTANCE.convert02(dictData)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataBaseVO.java index b3e14e8b4..691587a17 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataBaseVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataBaseVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.annotations.ApiModelProperty; import lombok.Data; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/dict/DictDataConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/dict/DictDataConvert.java index 215f252e7..3955e4566 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/dict/DictDataConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/dict/DictDataConvert.java @@ -1,13 +1,12 @@ package cn.iocoder.yudao.module.system.convert.dict; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.*; import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import java.util.Collection; import java.util.List; @Mapper @@ -29,6 +28,5 @@ public interface DictDataConvert { DictDataRespDTO convert02(DictDataDO bean); - List convertList03(Collection list); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java index 845f21db9..2a6b7cb84 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java @@ -23,6 +23,11 @@ public interface DictDataMapper extends BaseMapperX { .eq(DictDataDO::getValue, value)); } + default DictDataDO selectByDictTypeAndLabel(String dictType, String label) { + return selectOne(new LambdaQueryWrapper().eq(DictDataDO::getDictType, dictType) + .eq(DictDataDO::getLabel, label)); + } + default List selectByDictTypeAndValues(String dictType, Collection values) { return selectList(new LambdaQueryWrapper().eq(DictDataDO::getDictType, dictType) .in(DictDataDO::getValue, values)); @@ -46,7 +51,4 @@ public interface DictDataMapper extends BaseMapperX { .eqIfPresent(DictDataDO::getStatus, reqVO.getStatus())); } - @Select("SELECT COUNT(*) FROM system_dict_data WHERE update_time > #{maxUpdateTime}") - Long selectCountByUpdateTimeGt(Date maxUpdateTime); - } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dict/DictDataRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dict/DictDataRefreshConsumer.java deleted file mode 100644 index e466f96b2..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dict/DictDataRefreshConsumer.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.dict; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; -import cn.iocoder.yudao.module.system.mq.message.dict.DictDataRefreshMessage; -import cn.iocoder.yudao.module.system.service.dict.DictDataService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link DictDataRefreshMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class DictDataRefreshConsumer extends AbstractChannelMessageListener { - - @Resource - private DictDataService dictDataService; - - @Override - public void onMessage(DictDataRefreshMessage message) { - log.info("[onMessage][收到 DictData 刷新消息]"); - dictDataService.initLocalCache(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dict/DictDataRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dict/DictDataRefreshMessage.java deleted file mode 100644 index 01e5b8605..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dict/DictDataRefreshMessage.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.dict; - -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 字典数据数据刷新 Message - */ -@Data -@EqualsAndHashCode(callSuper = true) -public class DictDataRefreshMessage extends AbstractChannelMessage { - - @Override - public String getChannel() { - return "system.dict-data.refresh"; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dict/DictDataProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dict/DictDataProducer.java deleted file mode 100644 index fa90c8c42..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dict/DictDataProducer.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.dict; - -import cn.iocoder.yudao.module.system.mq.message.dict.DictDataRefreshMessage; -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * DictData 字典数据相关消息的 Producer - */ -@Component -public class DictDataProducer { - - @Resource - private RedisMQTemplate redisMQTemplate; - - /** - * 发送 {@link DictDataRefreshMessage} 消息 - */ - public void sendDictDataRefreshMessage() { - DictDataRefreshMessage message = new DictDataRefreshMessage(); - redisMQTemplate.send(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java index db1a1b624..610fe2a33 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java @@ -1,12 +1,11 @@ package cn.iocoder.yudao.module.system.service.dict; -import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataUpdateReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import java.util.Collection; import java.util.List; @@ -16,12 +15,7 @@ import java.util.List; * * @author ruoyi */ -public interface DictDataService extends DictDataFrameworkService { - - /** - * 初始化字典数据的本地缓存 - */ - void initLocalCache(); +public interface DictDataService { /** * 创建字典数据 @@ -94,4 +88,21 @@ public interface DictDataService extends DictDataFrameworkService { */ void validDictDatas(String dictType, Collection values); + /** + * 获得指定的字典数据 + * + * @param dictType 字典类型 + * @param value 字典数据值 + * @return 字典数据 + */ + DictDataDO getDictData(String dictType, String value); + + /** + * 解析获得指定的字典数据,从缓存中 + * + * @param dictType 字典类型 + * @param label 字典数据标签 + * @return 字典数据 + */ + DictDataDO parseDictData(String dictType, String label); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java index 3dc21d932..0cfdb2b99 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; @@ -13,20 +12,15 @@ import cn.iocoder.yudao.module.system.convert.dict.DictDataConvert; import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; -import cn.iocoder.yudao.module.system.mq.producer.dict.DictDataProducer; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableTable; import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.Collection; import java.util.Comparator; -import java.util.Date; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -47,88 +41,12 @@ public class DictDataServiceImpl implements DictDataService { .comparing(DictDataDO::getDictType) .thenComparingInt(DictDataDO::getSort); - /** - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 - */ - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; - @Resource private DictTypeService dictTypeService; @Resource private DictDataMapper dictDataMapper; - @Resource - private DictDataProducer dictDataProducer; - - /** - * 字典数据缓存,第二个 key 使用 label - * - * key1:字典类型 dictType - * key2:字典标签 label - */ - private ImmutableTable labelDictDataCache; - /** - * 字典数据缓存,第二个 key 使用 value - * - * key1:字典类型 dictType - * key2:字典值 value - */ - private ImmutableTable valueDictDataCache; - /** - * 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新 - */ - private volatile Date maxUpdateTime; - - @Override - @PostConstruct - public synchronized void initLocalCache() { - // 获取字典数据列表,如果有更新 - List dataList = loadDictDataIfUpdate(maxUpdateTime); - if (CollUtil.isEmpty(dataList)) { - return; - } - - // 构建缓存 - ImmutableTable.Builder labelDictDataBuilder = ImmutableTable.builder(); - ImmutableTable.Builder valueDictDataBuilder = ImmutableTable.builder(); - dataList.forEach(dictData -> { - labelDictDataBuilder.put(dictData.getDictType(), dictData.getLabel(), dictData); - valueDictDataBuilder.put(dictData.getDictType(), dictData.getValue(), dictData); - }); - labelDictDataCache = labelDictDataBuilder.build(); - valueDictDataCache = valueDictDataBuilder.build(); - maxUpdateTime = CollectionUtils.getMaxValue(dataList, DictDataDO::getUpdateTime); - log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size()); - } - - /** - * 如果字典数据发生变化,从数据库中获取最新的全量字典数据。 - * 如果未发生变化,则返回空 - * - * @param maxUpdateTime 当前字典数据的最大更新时间 - * @return 字典数据列表 - */ - private List loadDictDataIfUpdate(Date maxUpdateTime) { - // 第一步,判断是否要更新。 - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 - log.info("[loadDictDataIfUpdate][首次加载全量字典数据]"); - } else { // 判断数据库中是否有更新的字典数据 - if (dictDataMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { - return null; - } - log.info("[loadDictDataIfUpdate][增量加载全量字典数据]"); - } - // 第二步,如果有更新,则从数据库加载所有字典数据 - return dictDataMapper.selectList(); - } - - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) - public void schedulePeriodicRefresh() { - initLocalCache(); - } - @Override public List getDictDatas() { List list = dictDataMapper.selectList(); @@ -153,16 +71,6 @@ public class DictDataServiceImpl implements DictDataService { return dictDataMapper.selectById(id); } - @Override - public DictDataRespDTO getDictDataFromCache(String type, String value) { - return DictDataConvert.INSTANCE.convert02(valueDictDataCache.get(type, value)); - } - - @Override - public DictDataRespDTO parseDictDataFromCache(String type, String label) { - return DictDataConvert.INSTANCE.convert02(labelDictDataCache.get(type, label)); - } - @Override public Long createDictData(DictDataCreateReqVO reqVO) { // 校验正确性 @@ -171,9 +79,6 @@ public class DictDataServiceImpl implements DictDataService { // 插入字典类型 DictDataDO dictData = DictDataConvert.INSTANCE.convert(reqVO); dictDataMapper.insert(dictData); - - // 发送刷新消息 - dictDataProducer.sendDictDataRefreshMessage(); return dictData.getId(); } @@ -185,9 +90,6 @@ public class DictDataServiceImpl implements DictDataService { // 更新字典类型 DictDataDO updateObj = DictDataConvert.INSTANCE.convert(reqVO); dictDataMapper.updateById(updateObj); - - // 发送刷新消息 - dictDataProducer.sendDictDataRefreshMessage(); } @Override @@ -197,9 +99,6 @@ public class DictDataServiceImpl implements DictDataService { // 删除字典数据 dictDataMapper.deleteById(id); - - // 发送刷新消息 - dictDataProducer.sendDictDataRefreshMessage(); } @Override @@ -259,7 +158,8 @@ public class DictDataServiceImpl implements DictDataService { if (CollUtil.isEmpty(values)) { return; } - ImmutableMap dictDataMap = valueDictDataCache.row(dictType); + Map dictDataMap = CollectionUtils.convertMap( + dictDataMapper.selectByDictTypeAndValues(dictType, values), DictDataDO::getValue); // 校验 values.forEach(value -> { DictDataDO dictData = dictDataMap.get(value); @@ -272,4 +172,14 @@ public class DictDataServiceImpl implements DictDataService { }); } + @Override + public DictDataDO getDictData(String dictType, String value) { + return dictDataMapper.selectByDictTypeAndValue(dictType, value); + } + + @Override + public DictDataDO parseDictData(String dictType, String label) { + return dictDataMapper.selectByDictTypeAndLabel(dictType, label); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceTest.java index b20b33aaf..bf38bace1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceTest.java @@ -1,36 +1,32 @@ package cn.iocoder.yudao.module.system.service.dict; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataUpdateReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; -import cn.iocoder.yudao.module.system.mq.producer.dict.DictDataProducer; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import com.google.common.collect.ImmutableTable; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import java.util.Date; import java.util.List; import java.util.function.Consumer; -import static cn.hutool.core.bean.BeanUtil.getFieldValue; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; @Import(DictDataServiceImpl.class) public class DictDataServiceTest extends BaseDbUnitTest { @@ -42,39 +38,6 @@ public class DictDataServiceTest extends BaseDbUnitTest { private DictDataMapper dictDataMapper; @MockBean private DictTypeService dictTypeService; - @MockBean - private DictDataProducer dictDataProducer; - - /** - * 测试加载到新的字典数据的情况 - */ - @Test - @SuppressWarnings("unchecked") - public void testInitLocalCache() { - // mock 数据 - DictDataDO dictData01 = randomDictDataDO(); - dictDataMapper.insert(dictData01); - DictDataDO dictData02 = randomDictDataDO(); - dictDataMapper.insert(dictData02); - - // 调用 - dictDataService.initLocalCache(); - // 断言 labelDictDataCache 缓存 - ImmutableTable labelDictDataCache = - (ImmutableTable) getFieldValue(dictDataService, "labelDictDataCache"); - assertEquals(2, labelDictDataCache.size()); - assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel())); - assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel())); - // 断言 valueDictDataCache 缓存 - ImmutableTable valueDictDataCache = - (ImmutableTable) getFieldValue(dictDataService, "valueDictDataCache"); - assertEquals(2, valueDictDataCache.size()); - assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue())); - assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue())); - // 断言 maxUpdateTime 缓存 - Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime"); - assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime); - } @Test public void testGetDictDataPage() { @@ -148,8 +111,6 @@ public class DictDataServiceTest extends BaseDbUnitTest { // 校验记录的属性是否正确 DictDataDO dictData = dictDataMapper.selectById(dictDataId); assertPojoEquals(reqVO, dictData); - // 校验调用 - verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } @Test @@ -170,8 +131,6 @@ public class DictDataServiceTest extends BaseDbUnitTest { // 校验是否更新正确 DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, dictData); - // 校验调用 - verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } @Test @@ -186,8 +145,6 @@ public class DictDataServiceTest extends BaseDbUnitTest { dictDataService.deleteDictData(id); // 校验数据不存在了 assertNull(dictDataMapper.selectById(id)); - // 校验调用 - verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } @Test