diff --git a/pom.xml b/pom.xml
index d6bb8301a..24c7d6741 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 1.0.0
+ 1.0.0-snapshot
1.8
${java.version}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
index df54e7980..6de3f6b0c 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
@@ -167,7 +167,7 @@ public class SysUserController {
@PreAuthorize("@ss.hasPermission('system:user:import')")
public CommonResult importExcel(@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
- List list = ExcelUtils.raed(file, SysUserImportExcelVO.class);
+ List list = ExcelUtils.read(file, SysUserImportExcelVO.class);
return success(userService.importUsers(list, updateSupport));
}
diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index 5556b6be2..725fd1327 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -14,7 +14,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 1.0.0
+ 1.1.0-snapshot
2.4.5
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
index 0e9bfb4de..67d558f6b 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
@@ -39,7 +39,7 @@ public class ExcelUtils {
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
}
- public static List raed(MultipartFile file, Class head) throws IOException {
+ public static List read(MultipartFile file, Class head) throws IOException {
return EasyExcel.read(file.getInputStream(), head, null)
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
.doReadAllSync();
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
index a26de72cc..edfbc63dc 100644
--- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
@@ -1,14 +1,9 @@
package cn.iocoder.yudao.userserver;
-import cn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration;
-import cn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration;
-import cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-@SpringBootApplication(exclude = {
- YudaoDictAutoConfiguration.class,
-})
+@SpringBootApplication
public class UserServerApplication {
public static void main(String[] args) {
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java
new file mode 100644
index 000000000..02155f698
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.userserver.modules.system.convert.dict;
+
+import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface SysDictDataConvert {
+
+ SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class);
+
+ DictDataRespDTO convert02(SysDictDataDO bean);
+
+ List convertList03(Collection list);
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java
new file mode 100644
index 000000000..65ad8dc88
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules.system.convert;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java
new file mode 100644
index 000000000..eacec758f
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 字典数据表
+ *
+ * @author ruoyi
+ */
+@TableName("sys_dict_data")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysDictDataDO extends BaseDO {
+
+ /**
+ * 字典数据编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 字典排序
+ */
+ private Integer sort;
+ /**
+ * 字典标签
+ */
+ private String label;
+ /**
+ * 字典值
+ */
+ private String value;
+ /**
+ * 字典类型
+ *
+ * 冗余 {@link SysDictDataDO#getDictType()}
+ */
+ private String dictType;
+ /**
+ * 状态
+ *
+ * 枚举 {@link CommonStatusEnum}
+ */
+ private Integer status;
+ /**
+ * 备注
+ */
+ private String remark;
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java
new file mode 100644
index 000000000..0c99dcc95
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.dataobject;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java
new file mode 100644
index 000000000..928a53f7d
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict;
+
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Date;
+
+@Mapper
+public interface SysDictDataMapper extends BaseMapperX {
+
+ default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) {
+ return selectOne(new QueryWrapper().eq("dict_type", dictType)
+ .eq("value", value));
+ }
+
+ default int selectCountByDictType(String dictType) {
+ return selectCount("dict_type", dictType);
+ }
+
+ default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
+ return selectOne(new QueryWrapper().select("id")
+ .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
+ }
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java
new file mode 100644
index 000000000..a1bdeadcd
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.userserver.modules.system.dal.mysql;
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java
new file mode 100644
index 000000000..2efffe19e
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.userserver.modules.system.dict;
+
+import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
+
+/**
+ * 字典数据 Service 接口
+ *
+ * @author ruoyi
+ */
+public interface SysDictDataService extends DictDataFrameworkService {
+
+ /**
+ * 初始化字典数据的本地缓存
+ */
+ void initLocalCache();
+
+}
diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java
new file mode 100644
index 000000000..6e062eca7
--- /dev/null
+++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java
@@ -0,0 +1,122 @@
+package cn.iocoder.yudao.userserver.modules.system.dict.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.userserver.modules.system.convert.dict.SysDictDataConvert;
+import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
+import cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict.SysDictDataMapper;
+import cn.iocoder.yudao.userserver.modules.system.dict.SysDictDataService;
+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.Comparator;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 字典数据 Service 实现类
+ *
+ * @author ruoyi
+ */
+@Service
+@Slf4j
+public class SysDictDataServiceImpl implements SysDictDataService {
+
+ /**
+ * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
+ * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
+ */
+ private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
+
+ /**
+ * 字典数据缓存,第二个 key 使用 label
+ *
+ * key1:字典类型 dictType
+ * key2:字典标签 label
+ */
+ private ImmutableTable labelDictDataCache;
+ /**
+ * 字典数据缓存,第二个 key 使用 value
+ *
+ * key1:字典类型 dictType
+ * key2:字典值 value
+ */
+ private ImmutableTable valueDictDataCache;
+ /**
+ * 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新
+ */
+ private volatile Date maxUpdateTime;
+
+ @Resource
+ private SysDictDataMapper dictDataMapper;
+
+ @Override
+ @PostConstruct
+ public synchronized void initLocalCache() {
+ // 获取字典数据列表,如果有更新
+ List dataList = this.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();
+ assert dataList.size() > 0; // 断言,避免告警
+ maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
+ log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
+ }
+
+ @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
+ public void schedulePeriodicRefresh() {
+ initLocalCache();
+ }
+
+ /**
+ * 如果字典数据发生变化,从数据库中获取最新的全量字典数据。
+ * 如果未发生变化,则返回空
+ *
+ * @param maxUpdateTime 当前字典数据的最大更新时间
+ * @return 字典数据列表
+ */
+ private List loadDictDataIfUpdate(Date maxUpdateTime) {
+ // 第一步,判断是否要更新。
+ if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
+ log.info("[loadDictDataIfUpdate][首次加载全量字典数据]");
+ } else { // 判断数据库中是否有更新的字典数据
+ if (!dictDataMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
+ return null;
+ }
+ log.info("[loadDictDataIfUpdate][增量加载全量字典数据]");
+ }
+ // 第二步,如果有更新,则从数据库加载所有字典数据
+ return dictDataMapper.selectList();
+ }
+
+ @Override
+ public DictDataRespDTO getDictDataFromCache(String type, String value) {
+ return SysDictDataConvert.INSTANCE.convert02(valueDictDataCache.get(type, value));
+ }
+
+ @Override
+ public DictDataRespDTO parseDictDataFromCache(String type, String label) {
+ return SysDictDataConvert.INSTANCE.convert02(labelDictDataCache.get(type, label));
+ }
+
+ @Override
+ public List listDictDatasFromCache(String type) {
+ return SysDictDataConvert.INSTANCE.convertList03(labelDictDataCache.row(type).values());
+ }
+
+}