Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Y
yudao-cloud
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
hblj
yudao-cloud
Commits
31c24cd3
提交
31c24cd3
authored
6月 17, 2022
作者:
YunaiV
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
基于 Guava 实现 dict 字典数据的本地缓存
上级
0045602e
隐藏空白字符变更
内嵌
并排
正在显示
22 个修改的文件
包含
209 行增加
和
322 行删除
+209
-322
CacheUtils.java
...iocoder/yudao/framework/common/util/cache/CacheUtils.java
+1
-1
pom.xml
yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml
+20
-0
YudaoDictAutoConfiguration.java
...dao/framework/dict/config/YudaoDictAutoConfiguration.java
+3
-3
YudaoDictRpcAutoConfiguration.java
.../framework/dict/config/YudaoDictRpcAutoConfiguration.java
+15
-0
DictDataRespDTO.java
...ocoder/yudao/framework/dict/core/dto/DictDataRespDTO.java
+0
-33
package-info.java
...va/cn/iocoder/yudao/framework/dict/core/package-info.java
+4
-0
DictDataFrameworkService.java
...framework/dict/core/service/DictDataFrameworkService.java
+0
-27
DictFrameworkUtils.java
...er/yudao/framework/dict/core/util/DictFrameworkUtils.java
+56
-9
spring.factories
...ter-biz-dict/src/main/resources/META-INF/spring.factories
+1
-0
DictConvert.java
...coder/yudao/framework/excel/core/convert/DictConvert.java
+7
-8
DictDataApi.java
.../cn/iocoder/yudao/module/system/api/dict/DictDataApi.java
+19
-0
DictDataRespDTO.java
...der/yudao/module/system/api/dict/dto/DictDataRespDTO.java
+21
-0
DictDataApiImpl.java
...iocoder/yudao/module/system/api/dict/DictDataApiImpl.java
+15
-0
DictDataBaseVO.java
.../system/controller/admin/dict/vo/data/DictDataBaseVO.java
+2
-0
DictDataConvert.java
...der/yudao/module/system/convert/dict/DictDataConvert.java
+2
-4
DictDataMapper.java
...er/yudao/module/system/dal/mysql/dict/DictDataMapper.java
+5
-3
DictDataRefreshConsumer.java
...dule/system/mq/consumer/dict/DictDataRefreshConsumer.java
+0
-29
DictDataRefreshMessage.java
...module/system/mq/message/dict/DictDataRefreshMessage.java
+0
-19
DictDataProducer.java
...udao/module/system/mq/producer/dict/DictDataProducer.java
+0
-26
DictDataService.java
...der/yudao/module/system/service/dict/DictDataService.java
+19
-8
DictDataServiceImpl.java
...yudao/module/system/service/dict/DictDataServiceImpl.java
+13
-103
DictDataServiceTest.java
...yudao/module/system/service/dict/DictDataServiceTest.java
+6
-49
没有找到文件。
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
浏览文件 @
31c24cd3
...
...
@@ -19,7 +19,7 @@ public class CacheUtils {
// 只阻塞当前数据加载线程,其他线程返回旧值
.
refreshAfterWrite
(
duration
)
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
.
build
(
CacheLoader
.
asyncReloading
(
loader
,
Executors
.
new
SingleThreadExecutor
()));
.
build
(
CacheLoader
.
asyncReloading
(
loader
,
Executors
.
new
CachedThreadPool
()));
// TODO 芋艿:可能要思考下,未来要不要做成可配置
}
}
yudao-framework/yudao-spring-boot-starter-biz-dict/pom.xml
浏览文件 @
31c24cd3
...
...
@@ -26,5 +26,25 @@
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter
</artifactId>
</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>
</project>
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictAutoConfiguration.java
浏览文件 @
31c24cd3
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
(
DictData
FrameworkService
service
)
{
DictFrameworkUtils
.
init
(
service
);
public
DictFrameworkUtils
dictUtils
(
DictData
Api
dictDataApi
)
{
DictFrameworkUtils
.
init
(
dictDataApi
);
return
new
DictFrameworkUtils
();
}
...
...
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/config/YudaoDictRpcAutoConfiguration.java
0 → 100644
浏览文件 @
31c24cd3
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
{
}
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/dto/DictDataRespDTO.java
deleted
100644 → 0
浏览文件 @
0045602e
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
;
}
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/package-info.java
0 → 100644
浏览文件 @
31c24cd3
/**
* 占位
*/
package
cn
.
iocoder
.
yudao
.
framework
.
dict
.
core
;
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/service/DictDataFrameworkService.java
deleted
100644 → 0
浏览文件 @
0045602e
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
);
}
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java
浏览文件 @
31c24cd3
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
;
private
static
final
DictDataRespDTO
DICT_DATA_NULL
=
new
DictDataRespDTO
();
/**
* 针对 {@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
(
DictData
FrameworkService
service
)
{
DictFrameworkUtils
.
service
=
service
;
public
static
void
init
(
DictData
Api
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
();
}
}
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/resources/META-INF/spring.factories
浏览文件 @
31c24cd3
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.iocoder.yudao.framework.dict.config.YudaoDictRpcAutoConfiguration,\
cn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java
浏览文件 @
31c24cd3
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<Object> {
// 使用字典解析
String
type
=
getType
(
contentProperty
);
String
label
=
cellData
.
getStringValue
();
DictDataRespDTO
dictData
=
DictFrameworkUtils
.
parseDictDataFromCach
e
(
type
,
label
);
if
(
dictData
==
null
)
{
String
value
=
DictFrameworkUtils
.
parseDictDataValu
e
(
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<Object> {
// 使用字典格式化
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
)
{
...
...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java
浏览文件 @
31c24cd3
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
<
Boolean
>
validDictDatas
(
@RequestParam
(
"dictType"
)
String
dictType
,
@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
);
}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/dto/DictDataRespDTO.java
0 → 100644
浏览文件 @
31c24cd3
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
;
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java
浏览文件 @
31c24cd3
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
<
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
));
}
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataBaseVO.java
浏览文件 @
31c24cd3
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
;
...
...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/dict/DictDataConvert.java
浏览文件 @
31c24cd3
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
<
DictDataRespDTO
>
convertList03
(
Collection
<
DictDataDO
>
list
);
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java
浏览文件 @
31c24cd3
...
...
@@ -23,6 +23,11 @@ public interface DictDataMapper extends BaseMapperX<DictDataDO> {
.
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
)
{
return
selectList
(
new
LambdaQueryWrapper
<
DictDataDO
>().
eq
(
DictDataDO:
:
getDictType
,
dictType
)
.
in
(
DictDataDO:
:
getValue
,
values
));
...
...
@@ -46,7 +51,4 @@ public interface DictDataMapper extends BaseMapperX<DictDataDO> {
.
eqIfPresent
(
DictDataDO:
:
getStatus
,
reqVO
.
getStatus
()));
}
@Select
(
"SELECT COUNT(*) FROM system_dict_data WHERE update_time > #{maxUpdateTime}"
)
Long
selectCountByUpdateTimeGt
(
Date
maxUpdateTime
);
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/dict/DictDataRefreshConsumer.java
deleted
100644 → 0
浏览文件 @
0045602e
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
();
}
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/dict/DictDataRefreshMessage.java
deleted
100644 → 0
浏览文件 @
0045602e
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"
;
}
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/dict/DictDataProducer.java
deleted
100644 → 0
浏览文件 @
0045602e
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
);
}
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java
浏览文件 @
31c24cd3
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
<
String
>
values
);
/**
* 获得指定的字典数据
*
* @param dictType 字典类型
* @param value 字典数据值
* @return 字典数据
*/
DictDataDO
getDictData
(
String
dictType
,
String
value
);
/**
* 解析获得指定的字典数据,从缓存中
*
* @param dictType 字典类型
* @param label 字典数据标签
* @return 字典数据
*/
DictDataDO
parseDictData
(
String
dictType
,
String
label
);
}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java
浏览文件 @
31c24cd3
...
...
@@ -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
<
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
public
List
<
DictDataDO
>
getDictDatas
()
{
List
<
DictDataDO
>
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
<
String
,
DictDataDO
>
dictDataMap
=
valueDictDataCache
.
row
(
dictType
);
Map
<
String
,
DictDataDO
>
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
);
}
}
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceTest.java
浏览文件 @
31c24cd3
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
<
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
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
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论