基于 Guava 实现 dict 字典数据的本地缓存
This commit is contained in:
parent
0045602e6b
commit
31c24cd340
@ -19,7 +19,7 @@ public class CacheUtils {
|
|||||||
// 只阻塞当前数据加载线程,其他线程返回旧值
|
// 只阻塞当前数据加载线程,其他线程返回旧值
|
||||||
.refreshAfterWrite(duration)
|
.refreshAfterWrite(duration)
|
||||||
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
||||||
.build(CacheLoader.asyncReloading(loader, Executors.newSingleThreadExecutor()));
|
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,5 +26,25 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- RPC 远程调用相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.cloud</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 业务组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.cloud</groupId>
|
||||||
|
<artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 -->
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.iocoder.yudao.framework.dict.config;
|
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.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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@ -10,8 +10,8 @@ public class YudaoDictAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
@SuppressWarnings("InstantiationOfUtilityClass")
|
||||||
public DictFrameworkUtils dictUtils(DictDataFrameworkService service) {
|
public DictFrameworkUtils dictUtils(DictDataApi dictDataApi) {
|
||||||
DictFrameworkUtils.init(service);
|
DictFrameworkUtils.init(dictDataApi);
|
||||||
return new DictFrameworkUtils();
|
return new DictFrameworkUtils();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
}
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 占位
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.framework.dict.core;
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +1,75 @@
|
|||||||
package cn.iocoder.yudao.framework.dict.core.util;
|
package cn.iocoder.yudao.framework.dict.core.util;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典工具类
|
* 字典工具类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DictFrameworkUtils {
|
public class DictFrameworkUtils {
|
||||||
|
|
||||||
private static DictDataFrameworkService service;
|
private static DictDataApi dictDataApi;
|
||||||
|
|
||||||
public static void init(DictDataFrameworkService service) {
|
private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO();
|
||||||
DictFrameworkUtils.service = service;
|
|
||||||
|
/**
|
||||||
|
* 针对 {@link #getDictDataLabel(String, String)} 的缓存
|
||||||
|
*/
|
||||||
|
private static final LoadingCache<KeyValue<String, String>, DictDataRespDTO> getDictDataCache = CacheUtils.buildAsyncReloadingCache(
|
||||||
|
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||||
|
new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DictDataRespDTO load(KeyValue<String, String> key) {
|
||||||
|
CommonResult<DictDataRespDTO> 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<KeyValue<String, String>, DictDataRespDTO> parseDictDataCache = CacheUtils.buildAsyncReloadingCache(
|
||||||
|
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||||
|
new CacheLoader<KeyValue<String, String>, DictDataRespDTO>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DictDataRespDTO load(KeyValue<String, String> key) {
|
||||||
|
CommonResult<DictDataRespDTO> 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 成功]");
|
log.info("[init][初始化 DictFrameworkUtils 成功]");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DictDataRespDTO getDictDataFromCache(String type, String value) {
|
@SneakyThrows
|
||||||
return service.getDictDataFromCache(type, value);
|
public static String getDictDataLabel(String dictType, String value) {
|
||||||
|
return getDictDataCache.get(new KeyValue<>(dictType, value)).getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DictDataRespDTO parseDictDataFromCache(String type, String label) {
|
@SneakyThrows
|
||||||
return service.parseDictDataFromCache(type, label);
|
public static String parseDictDataValue(String dictType, String label) {
|
||||||
|
return parseDictDataCache.get(new KeyValue<>(dictType, label)).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
cn.iocoder.yudao.framework.dict.config.YudaoDictRpcAutoConfiguration,\
|
||||||
cn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration
|
cn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cn.iocoder.yudao.framework.excel.core.convert;
|
package cn.iocoder.yudao.framework.excel.core.convert;
|
||||||
|
|
||||||
import cn.hutool.core.convert.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.dict.core.util.DictFrameworkUtils;
|
||||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||||
import com.alibaba.excel.converters.Converter;
|
import com.alibaba.excel.converters.Converter;
|
||||||
@ -12,7 +11,7 @@ import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excel {@link DictDataRespDTO} 数据字典转换器
|
* Excel 数据字典转换器
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@ -35,14 +34,14 @@ public class DictConvert implements Converter<Object> {
|
|||||||
// 使用字典解析
|
// 使用字典解析
|
||||||
String type = getType(contentProperty);
|
String type = getType(contentProperty);
|
||||||
String label = cellData.getStringValue();
|
String label = cellData.getStringValue();
|
||||||
DictDataRespDTO dictData = DictFrameworkUtils.parseDictDataFromCache(type, label);
|
String value = DictFrameworkUtils.parseDictDataValue(type, label);
|
||||||
if (dictData == null) {
|
if (value == null) {
|
||||||
log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label);
|
log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 将 String 的 value 转换成对应的属性
|
// 将 String 的 value 转换成对应的属性
|
||||||
Class<?> fieldClazz = contentProperty.getField().getType();
|
Class<?> fieldClazz = contentProperty.getField().getType();
|
||||||
return Convert.convert(fieldClazz, dictData.getValue());
|
return Convert.convert(fieldClazz, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -56,13 +55,13 @@ public class DictConvert implements Converter<Object> {
|
|||||||
// 使用字典格式化
|
// 使用字典格式化
|
||||||
String type = getType(contentProperty);
|
String type = getType(contentProperty);
|
||||||
String value = String.valueOf(object);
|
String value = String.valueOf(object);
|
||||||
DictDataRespDTO dictData = DictFrameworkUtils.getDictDataFromCache(type, value);
|
String label = DictFrameworkUtils.getDictDataLabel(type, value);
|
||||||
if (dictData == null) {
|
if (label == null) {
|
||||||
log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value);
|
log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value);
|
||||||
return new CellData<>("");
|
return new CellData<>("");
|
||||||
}
|
}
|
||||||
// 生成 Excel 小表格
|
// 生成 Excel 小表格
|
||||||
return new CellData<>(dictData.getLabel());
|
return new CellData<>(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getType(ExcelContentProperty contentProperty) {
|
private static String getType(ExcelContentProperty contentProperty) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.yudao.module.system.api.dict;
|
package cn.iocoder.yudao.module.system.api.dict;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
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 cn.iocoder.yudao.module.system.enums.ApiConstants;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
@ -27,4 +28,22 @@ public interface DictDataApi {
|
|||||||
CommonResult<Boolean> validDictDatas(@RequestParam("dictType") String dictType,
|
CommonResult<Boolean> validDictDatas(@RequestParam("dictType") String dictType,
|
||||||
@RequestParam("values") Collection<String> values);
|
@RequestParam("values") Collection<String> 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<DictDataRespDTO> 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<DictDataRespDTO> parseDictData(@RequestParam("dictType") String dictType,
|
||||||
|
@RequestParam("label") String label);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package cn.iocoder.yudao.module.system.api.dict;
|
package cn.iocoder.yudao.module.system.api.dict;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
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 cn.iocoder.yudao.module.system.service.dict.DictDataService;
|
||||||
import org.apache.dubbo.config.annotation.DubboService;
|
import org.apache.dubbo.config.annotation.DubboService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -27,4 +30,16 @@ public class DictDataApiImpl implements DictDataApi {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<DictDataRespDTO> getDictData(String dictType, String value) {
|
||||||
|
DictDataDO dictData = dictDataService.getDictData(dictType, value);
|
||||||
|
return success(DictDataConvert.INSTANCE.convert02(dictData));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<DictDataRespDTO> parseDictData(String dictType, String label) {
|
||||||
|
DictDataDO dictData = dictDataService.parseDictData(dictType, label);
|
||||||
|
return success(DictDataConvert.INSTANCE.convert02(dictData));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data;
|
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 io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package cn.iocoder.yudao.module.system.convert.dict;
|
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.controller.admin.dict.vo.data.*;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
|
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.Mapper;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
@ -29,6 +28,5 @@ public interface DictDataConvert {
|
|||||||
|
|
||||||
DictDataRespDTO convert02(DictDataDO bean);
|
DictDataRespDTO convert02(DictDataDO bean);
|
||||||
|
|
||||||
List<DictDataRespDTO> convertList03(Collection<DictDataDO> list);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,11 @@ public interface DictDataMapper extends BaseMapperX<DictDataDO> {
|
|||||||
.eq(DictDataDO::getValue, value));
|
.eq(DictDataDO::getValue, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default DictDataDO selectByDictTypeAndLabel(String dictType, String label) {
|
||||||
|
return selectOne(new LambdaQueryWrapper<DictDataDO>().eq(DictDataDO::getDictType, dictType)
|
||||||
|
.eq(DictDataDO::getLabel, label));
|
||||||
|
}
|
||||||
|
|
||||||
default List<DictDataDO> selectByDictTypeAndValues(String dictType, Collection<String> values) {
|
default List<DictDataDO> selectByDictTypeAndValues(String dictType, Collection<String> values) {
|
||||||
return selectList(new LambdaQueryWrapper<DictDataDO>().eq(DictDataDO::getDictType, dictType)
|
return selectList(new LambdaQueryWrapper<DictDataDO>().eq(DictDataDO::getDictType, dictType)
|
||||||
.in(DictDataDO::getValue, values));
|
.in(DictDataDO::getValue, values));
|
||||||
@ -46,7 +51,4 @@ public interface DictDataMapper extends BaseMapperX<DictDataDO> {
|
|||||||
.eqIfPresent(DictDataDO::getStatus, reqVO.getStatus()));
|
.eqIfPresent(DictDataDO::getStatus, reqVO.getStatus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Select("SELECT COUNT(*) FROM system_dict_data WHERE update_time > #{maxUpdateTime}")
|
|
||||||
Long selectCountByUpdateTimeGt(Date maxUpdateTime);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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<DictDataRefreshMessage> {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private DictDataService dictDataService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessage(DictDataRefreshMessage message) {
|
|
||||||
log.info("[onMessage][收到 DictData 刷新消息]");
|
|
||||||
dictDataService.initLocalCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +1,11 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.dict;
|
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.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.DictDataCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
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.DictDataPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataUpdateReqVO;
|
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.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -16,12 +15,7 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
public interface DictDataService extends DictDataFrameworkService {
|
public interface DictDataService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化字典数据的本地缓存
|
|
||||||
*/
|
|
||||||
void initLocalCache();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建字典数据
|
* 创建字典数据
|
||||||
@ -94,4 +88,21 @@ public interface DictDataService extends DictDataFrameworkService {
|
|||||||
*/
|
*/
|
||||||
void validDictDatas(String dictType, Collection<String> values);
|
void validDictDatas(String dictType, Collection<String> values);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定的字典数据
|
||||||
|
*
|
||||||
|
* @param dictType 字典类型
|
||||||
|
* @param value 字典数据值
|
||||||
|
* @return 字典数据
|
||||||
|
*/
|
||||||
|
DictDataDO getDictData(String dictType, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析获得指定的字典数据,从缓存中
|
||||||
|
*
|
||||||
|
* @param dictType 字典类型
|
||||||
|
* @param label 字典数据标签
|
||||||
|
* @return 字典数据
|
||||||
|
*/
|
||||||
|
DictDataDO parseDictData(String dictType, String label);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
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.DictDataCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
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.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.DictDataDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
|
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.dal.mysql.dict.DictDataMapper;
|
||||||
import cn.iocoder.yudao.module.system.mq.producer.dict.DictDataProducer;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableTable;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
@ -47,88 +41,12 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
.comparing(DictDataDO::getDictType)
|
.comparing(DictDataDO::getDictType)
|
||||||
.thenComparingInt(DictDataDO::getSort);
|
.thenComparingInt(DictDataDO::getSort);
|
||||||
|
|
||||||
/**
|
|
||||||
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
|
|
||||||
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
|
|
||||||
*/
|
|
||||||
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DictTypeService dictTypeService;
|
private DictTypeService dictTypeService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DictDataMapper dictDataMapper;
|
private DictDataMapper dictDataMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private DictDataProducer dictDataProducer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字典数据缓存,第二个 key 使用 label
|
|
||||||
*
|
|
||||||
* key1:字典类型 dictType
|
|
||||||
* key2:字典标签 label
|
|
||||||
*/
|
|
||||||
private ImmutableTable<String, String, DictDataDO> labelDictDataCache;
|
|
||||||
/**
|
|
||||||
* 字典数据缓存,第二个 key 使用 value
|
|
||||||
*
|
|
||||||
* key1:字典类型 dictType
|
|
||||||
* key2:字典值 value
|
|
||||||
*/
|
|
||||||
private ImmutableTable<String, String, DictDataDO> valueDictDataCache;
|
|
||||||
/**
|
|
||||||
* 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新
|
|
||||||
*/
|
|
||||||
private volatile Date maxUpdateTime;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@PostConstruct
|
|
||||||
public synchronized void initLocalCache() {
|
|
||||||
// 获取字典数据列表,如果有更新
|
|
||||||
List<DictDataDO> dataList = loadDictDataIfUpdate(maxUpdateTime);
|
|
||||||
if (CollUtil.isEmpty(dataList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建缓存
|
|
||||||
ImmutableTable.Builder<String, String, DictDataDO> labelDictDataBuilder = ImmutableTable.builder();
|
|
||||||
ImmutableTable.Builder<String, String, DictDataDO> 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<DictDataDO> 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
|
@Override
|
||||||
public List<DictDataDO> getDictDatas() {
|
public List<DictDataDO> getDictDatas() {
|
||||||
List<DictDataDO> list = dictDataMapper.selectList();
|
List<DictDataDO> list = dictDataMapper.selectList();
|
||||||
@ -153,16 +71,6 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
return dictDataMapper.selectById(id);
|
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
|
@Override
|
||||||
public Long createDictData(DictDataCreateReqVO reqVO) {
|
public Long createDictData(DictDataCreateReqVO reqVO) {
|
||||||
// 校验正确性
|
// 校验正确性
|
||||||
@ -171,9 +79,6 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
// 插入字典类型
|
// 插入字典类型
|
||||||
DictDataDO dictData = DictDataConvert.INSTANCE.convert(reqVO);
|
DictDataDO dictData = DictDataConvert.INSTANCE.convert(reqVO);
|
||||||
dictDataMapper.insert(dictData);
|
dictDataMapper.insert(dictData);
|
||||||
|
|
||||||
// 发送刷新消息
|
|
||||||
dictDataProducer.sendDictDataRefreshMessage();
|
|
||||||
return dictData.getId();
|
return dictData.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +90,6 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
// 更新字典类型
|
// 更新字典类型
|
||||||
DictDataDO updateObj = DictDataConvert.INSTANCE.convert(reqVO);
|
DictDataDO updateObj = DictDataConvert.INSTANCE.convert(reqVO);
|
||||||
dictDataMapper.updateById(updateObj);
|
dictDataMapper.updateById(updateObj);
|
||||||
|
|
||||||
// 发送刷新消息
|
|
||||||
dictDataProducer.sendDictDataRefreshMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -197,9 +99,6 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
|
|
||||||
// 删除字典数据
|
// 删除字典数据
|
||||||
dictDataMapper.deleteById(id);
|
dictDataMapper.deleteById(id);
|
||||||
|
|
||||||
// 发送刷新消息
|
|
||||||
dictDataProducer.sendDictDataRefreshMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -259,7 +158,8 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
if (CollUtil.isEmpty(values)) {
|
if (CollUtil.isEmpty(values)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ImmutableMap<String, DictDataDO> dictDataMap = valueDictDataCache.row(dictType);
|
Map<String, DictDataDO> dictDataMap = CollectionUtils.convertMap(
|
||||||
|
dictDataMapper.selectByDictTypeAndValues(dictType, values), DictDataDO::getValue);
|
||||||
// 校验
|
// 校验
|
||||||
values.forEach(value -> {
|
values.forEach(value -> {
|
||||||
DictDataDO dictData = dictDataMap.get(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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,32 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.dict;
|
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.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.DictDataCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
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.DictDataPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataUpdateReqVO;
|
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.dataobject.dict.DictTypeDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper;
|
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.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
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.assertPojoEquals;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
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.framework.test.core.util.RandomUtils.*;
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@Import(DictDataServiceImpl.class)
|
@Import(DictDataServiceImpl.class)
|
||||||
public class DictDataServiceTest extends BaseDbUnitTest {
|
public class DictDataServiceTest extends BaseDbUnitTest {
|
||||||
@ -42,39 +38,6 @@ public class DictDataServiceTest extends BaseDbUnitTest {
|
|||||||
private DictDataMapper dictDataMapper;
|
private DictDataMapper dictDataMapper;
|
||||||
@MockBean
|
@MockBean
|
||||||
private DictTypeService dictTypeService;
|
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<String, String, DictDataDO> labelDictDataCache =
|
|
||||||
(ImmutableTable<String, String, DictDataDO>) 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<String, String, DictDataDO> valueDictDataCache =
|
|
||||||
(ImmutableTable<String, String, DictDataDO>) 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
|
@Test
|
||||||
public void testGetDictDataPage() {
|
public void testGetDictDataPage() {
|
||||||
@ -148,8 +111,6 @@ public class DictDataServiceTest extends BaseDbUnitTest {
|
|||||||
// 校验记录的属性是否正确
|
// 校验记录的属性是否正确
|
||||||
DictDataDO dictData = dictDataMapper.selectById(dictDataId);
|
DictDataDO dictData = dictDataMapper.selectById(dictDataId);
|
||||||
assertPojoEquals(reqVO, dictData);
|
assertPojoEquals(reqVO, dictData);
|
||||||
// 校验调用
|
|
||||||
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -170,8 +131,6 @@ public class DictDataServiceTest extends BaseDbUnitTest {
|
|||||||
// 校验是否更新正确
|
// 校验是否更新正确
|
||||||
DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
|
DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
|
||||||
assertPojoEquals(reqVO, dictData);
|
assertPojoEquals(reqVO, dictData);
|
||||||
// 校验调用
|
|
||||||
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -186,8 +145,6 @@ public class DictDataServiceTest extends BaseDbUnitTest {
|
|||||||
dictDataService.deleteDictData(id);
|
dictDataService.deleteDictData(id);
|
||||||
// 校验数据不存在了
|
// 校验数据不存在了
|
||||||
assertNull(dictDataMapper.selectById(id));
|
assertNull(dictDataMapper.selectById(id));
|
||||||
// 校验调用
|
|
||||||
verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user