本文带你深入解析基于枚举 + 动态配置构建的通用字典系统,适用于中后台管理系统的数据字典需求。
一、字典模块的核心目标
✅ 统一字典管理 :支持枚举类字典、数据库字典、自定义字典。
✅ 动态扩展能力 :允许第三方业务模块注册自定义字典。
✅ 标签/值双向转换 :提供 dictType -> value <-> label
转换接口。
✅ 缓存优化 :提升高频访问性能,同时支持缓存清理。
✅ 导入导出功能:支持 Excel 导入导出,便于维护。
二、字典模型结构详解
核心实体类说明
实体 | 描述 |
---|---|
Dict | 字典主表,存储字典编码、名称、数据类型等元信息 |
DictItem | 字典项明细,每个字典可包含多个字典项 |
数据库结构图解(ER 图)

三、字典分类与来源
支持的字典类型
类型 | 来源 | 示例 |
---|---|---|
枚举类字典 | Java 枚举类注解标记 | 如 SexEnum |
自定义字典 | 业务模块实现 CustomDictService 接口 | 如订单状态、支付方式 |
数据库字典 | 存储在 sys_dict 和 sys_dict_item 表中 |
用户手动配置的业务字典 |
示例:枚举类字典
java
@DictEnum(key = "sex", name = "性别")
public enum SexEnum implements CodedEnum {
MAN(1, "男"),
WOMAN(2, "女"),
UN_KNOW(3, "未知");
}
四、核心服务接口设计
DictService
核心接口方法
java
public interface DictService extends IService<Dict> {
boolean save(DictParam param);
boolean update(DictParam param);
CommonPage<DictVO> page(DictPageParam param);
DictVO findById(Long id);
List<cn.hutool.core.lang.Dict> getByDictType(String dictType);
String toLabel(String dictType, Object value);
Object toValue(String dictType, String label);
void clearCache();
void importTo(InputStream inputStream);
}
🔗 源码地址:DictService.java
五、缓存机制详解
使用的缓存工具
- ✅ 缓存工具类:RedisUtil.delayedDoubleRemove
- ✅ 字典缓存类:DictCache
示例代码:
java
@Override
public boolean update(DictParam param) {
LowCodeServiceUtil.checkUnique(baseMapper,"code",param.getCode(),param.getId(),"唯一编码已存在,请检查code参数");
Dict dict = new Dict();
BeanUtil.copyProperties(param, dict);
return RedisUtil.delayedDoubleRemove(dictCache,(Dict object)->{
boolean success = super.updateById(object);
if(success && CollectionUtil.isNotEmpty(param.getDictItemList())){
List<Long> ids = new ArrayList<>();
param.getDictItemList().forEach(item->{
item.setDictId(dict.getId());
try{
dictItemService.save(item);
} catch (ServiceException e){
DictItem dictItem = dictItemService.getOne(Wrappers.lambdaQuery(DictItem.class).eq(DictItem::getCode,item.getCode()));
item.setId(dictItem.getId());
dictItemService.update(item);
}
ids.add(item.getId());
});
dictItemService.remove(
Wrappers.lambdaQuery(DictItem.class)
.eq(DictItem::getDictId,dict.getId())
.notIn(DictItem::getId,ids));
}
return success;
},dict,"code");
}
六、缓存工具类 RedisUtil.delayedDoubleRemove
详解
作用:延时双删策略防止缓存穿透 & 防止并发更新导致脏读
实现逻辑如下:
- 第一次删除缓存;
- 执行数据库更新操作;
- 延迟一段时间后再次删除缓存。
源码结构(示意):
java
public static <T> boolean delayedDoubleRemove(AbstractRedisCacheOperator<T> cacheOperator,
Function<T, Boolean> operation,
T data, String keySuffix) {
// 第一次删除缓存
cacheOperator.delete(keySuffix);
// 执行数据库更新操作
boolean result = operation.apply(data);
// 延迟 500ms 后再次删除缓存
new Timer().schedule(new TimerTask() {
@Override
public void run() {
cacheOperator.delete(keySuffix);
}
}, 500);
return result;
}
优势:
🧠 避免缓存和数据库不一致问题
🔒 防止并发更新造成缓存污染
⏱️ 合理利用时间窗口,降低缓存击穿风险
七、DictCache 缓存实现细节
缓存 key 构建规则
java
@Override
public String getCommonKeyPrefix() {
return "sys_dict:";
}
示例缓存 key:
plain
sys_dict:sex
sys_dict:wf_assignment_handler
缓存有效期
默认永久生效,有变更后会自动更新。
八、字典下拉接口 /sys/dict/getByDictType
详解
接口路径
java
@PostMapping("/sys/dict/getByDictType")
@ApiOperation(value = "字典下拉")
public CommonResult<List<Dict>> getByDictType(@RequestBody @Validated({DictParam.DictType.class}) DictParam param) {
return CommonResult.data(dictService.getByDictType(param.getDictType()));
}
🔗 接口文件地址:DictController.java
对应服务方法
java
@Override
public List<cn.hutool.core.lang.Dict> getByDictType(String dictType) {
DictModel res = dictCache.get(dictType);
if(ObjectUtil.isNotNull(res)) return dictModelToDictList(res);
DictModel dictModel;
DictDataType dictDataType;
Dict dict = this.getOne(Wrappers.lambdaQuery(Dict.class).eq(Dict::getCode, dictType));
if (ObjectUtil.isNull(dict)) {
// 空,则使用枚举类
dictModel = dictScanner.getDictMap().get(dictType);
if (dictModel == null) {
String[] customDictServiceArr = SpringUtil.getBeanNamesForType(CustomDictService.class);
for (String name : customDictServiceArr) {
CustomDictService customDictService = SpringUtil.getBean(name);
Map<String, Object> args = new HashMap<>();
args.put("dictType", dictType);
dictModel = customDictService.getByDictKey(args);
if(dictModel != null) {
break;
}
}
}
if (dictModel == null) {
return new ArrayList<>();
} else {
return dictModelToDictList(dictModel);
}
} else {
dictDataType = CodedEnum.codeOf(DictDataType.class,dict.getDataType()).orElse(DictDataType.STRING);
}
return dictItemService.getDictItemListByDictId(dict.getId(), dictDataType);
}
🔗 服务实现地址:DictServiceImpl.java
该方法用于根据字典类型 dictType
获取对应的字典项列表。支持多种数据来源:
- 缓存命中;
- 数据库字典匹配;
- 枚举类字典匹配;
- 自定义字典匹配;
- 默认返回空列表。
九、字典标签和值互转的 API 详解
在 mldong 项目中,为了方便前端与后端之间的字典数据转换,提供了两个实用的 API 方法:
toLabel(String dictType, Object value)
:将字典值转换为对应的标签(label);toValue(String dictType, String label)
:将字典标签转换为对应的值(value);
此外,在 LowCodeServiceUtil
中还封装了静态方法,支持全局调用这些功能:
dictValueToLabel(String dictType, Object value)
dictLabelToValue(String dictType, String label)
这两个方法广泛应用于表单展示、数据回显、日志显示等场景,能够提升系统的可读性和易用性。
方法定义
✅ toLabel(...)
java
@Override
public String toLabel(String dictType, Object value) {
if(value == null) return null;
List<cn.hutool.core.lang.Dict> dicts = getByDictType(dictType);
for (cn.hutool.core.lang.Dict dict : dicts) {
if (dict.getStr(CommonConstant.VALUE).equals(StrUtil.toString(value))) {
return dict.getStr(CommonConstant.LABEL);
}
}
return StrUtil.toString(value);
}
🔗 文件路径:DictServiceImpl.java
✅ toValue(...)
java
@Override
public Object toValue(String dictType, String label) {
if(StrUtil.isEmpty(label)) return null;
List<cn.hutool.core.lang.Dict> dicts = getByDictType(dictType);
for (cn.hutool.core.lang.Dict dict : dicts) {
if (dict.getStr(CommonConstant.LABEL).equals(label)) {
return dict.get(CommonConstant.VALUE);
}
}
return label;
}
🔗 文件路径:DictServiceImpl.java
十、总结与建议
✅ 已有优势:
- 支持枚举类、数据库、自定义三种字典来源;
- 提供完善的标签/值转换 API;
- 内置缓存机制,提升高频访问性能;
- 支持 Excel 导入导出,便于维护;
- 接口权限控制粒度细,支持 OR/AND 组合;
- 可扩展性强,支持业务模块自定义字典。
💡 可优化方向:
- 字段级字典绑定;
- 字典版本管理;
- 多语言支持;
- 可视化字典配置界面;
- 字典审计日志;
- 异步刷新缓存;
- 引入本地缓存 + Redis 二级缓存架构。
十一、结语
欢迎关注【mldong】开源项目,后续将持续分享字典扩展、低代码平台构建等内容。
如果你也想打造一个灵活可扩展的字典系统,欢迎 Star 我们的开源项目 mldong。
更多关于字典、低代码平台构建的内容,欢迎关注【mldong】公众号,第一时间获取更新通知!
📌 如需获取完整源码,请访问 Gitee 地址: