Commit 0439a5505afdde71ec3543d708349bfeba776719

Authored by YunaiV
1 parent 15e2c094

初始化数据字典

@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 19 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
20 20
21 <properties> 21 <properties>
22 - <revision>1.0.0</revision> 22 + <revision>1.0.0-snapshot</revision>
23 <!-- Maven 相关 --> 23 <!-- Maven 相关 -->
24 <java.version>1.8</java.version> 24 <java.version>1.8</java.version>
25 <maven.compiler.source>${java.version}</maven.compiler.source> 25 <maven.compiler.source>${java.version}</maven.compiler.source>
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/SysUserController.java
@@ -167,7 +167,7 @@ public class SysUserController { @@ -167,7 +167,7 @@ public class SysUserController {
167 @PreAuthorize("@ss.hasPermission('system:user:import')") 167 @PreAuthorize("@ss.hasPermission('system:user:import')")
168 public CommonResult<SysUserImportRespVO> importExcel(@RequestParam("file") MultipartFile file, 168 public CommonResult<SysUserImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
169 @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { 169 @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
170 - List<SysUserImportExcelVO> list = ExcelUtils.raed(file, SysUserImportExcelVO.class); 170 + List<SysUserImportExcelVO> list = ExcelUtils.read(file, SysUserImportExcelVO.class);
171 return success(userService.importUsers(list, updateSupport)); 171 return success(userService.importUsers(list, updateSupport));
172 } 172 }
173 173
yudao-dependencies/pom.xml
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 14 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
15 15
16 <properties> 16 <properties>
17 - <revision>1.0.0</revision> 17 + <revision>1.1.0-snapshot</revision>
18 <!-- 统一依赖管理 --> 18 <!-- 统一依赖管理 -->
19 <spring.boot.version>2.4.5</spring.boot.version> 19 <spring.boot.version>2.4.5</spring.boot.version>
20 <!-- Web 相关 --> 20 <!-- Web 相关 -->
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 { @@ -39,7 +39,7 @@ public class ExcelUtils {
39 response.setContentType("application/vnd.ms-excel;charset=UTF-8"); 39 response.setContentType("application/vnd.ms-excel;charset=UTF-8");
40 } 40 }
41 41
42 - public static <T> List<T> raed(MultipartFile file, Class<T> head) throws IOException { 42 + public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
43 return EasyExcel.read(file.getInputStream(), head, null) 43 return EasyExcel.read(file.getInputStream(), head, null)
44 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 44 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
45 .doReadAllSync(); 45 .doReadAllSync();
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/UserServerApplication.java
1 package cn.iocoder.yudao.userserver; 1 package cn.iocoder.yudao.userserver;
2 2
3 -import cn.iocoder.yudao.framework.dict.config.YudaoDictAutoConfiguration;  
4 -import cn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration;  
5 -import cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter;  
6 import org.springframework.boot.SpringApplication; 3 import org.springframework.boot.SpringApplication;
7 import org.springframework.boot.autoconfigure.SpringBootApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
8 5
9 -@SpringBootApplication(exclude = {  
10 - YudaoDictAutoConfiguration.class,  
11 -}) 6 +@SpringBootApplication
12 public class UserServerApplication { 7 public class UserServerApplication {
13 8
14 public static void main(String[] args) { 9 public static void main(String[] args) {
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/dict/SysDictDataConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.convert.dict;
  2 +
  3 +import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
  4 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
  5 +import org.mapstruct.Mapper;
  6 +import org.mapstruct.factory.Mappers;
  7 +
  8 +import java.util.Collection;
  9 +import java.util.List;
  10 +
  11 +@Mapper
  12 +public interface SysDictDataConvert {
  13 +
  14 + SysDictDataConvert INSTANCE = Mappers.getMapper(SysDictDataConvert.class);
  15 +
  16 + DictDataRespDTO convert02(SysDictDataDO bean);
  17 +
  18 + List<DictDataRespDTO> convertList03(Collection<SysDictDataDO> list);
  19 +
  20 +}
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/convert/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.convert;
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/dict/SysDictDataDO.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +
  10 +/**
  11 + * 字典数据表
  12 + *
  13 + * @author ruoyi
  14 + */
  15 +@TableName("sys_dict_data")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +public class SysDictDataDO extends BaseDO {
  19 +
  20 + /**
  21 + * 字典数据编号
  22 + */
  23 + @TableId
  24 + private Long id;
  25 + /**
  26 + * 字典排序
  27 + */
  28 + private Integer sort;
  29 + /**
  30 + * 字典标签
  31 + */
  32 + private String label;
  33 + /**
  34 + * 字典值
  35 + */
  36 + private String value;
  37 + /**
  38 + * 字典类型
  39 + *
  40 + * 冗余 {@link SysDictDataDO#getDictType()}
  41 + */
  42 + private String dictType;
  43 + /**
  44 + * 状态
  45 + *
  46 + * 枚举 {@link CommonStatusEnum}
  47 + */
  48 + private Integer status;
  49 + /**
  50 + * 备注
  51 + */
  52 + private String remark;
  53 +
  54 +}
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/dataobject/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.dal.dataobject;
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/dict/SysDictDataMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict;
  2 +
  3 +
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
  6 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +
  9 +import java.util.Date;
  10 +
  11 +@Mapper
  12 +public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> {
  13 +
  14 + default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) {
  15 + return selectOne(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType)
  16 + .eq("value", value));
  17 + }
  18 +
  19 + default int selectCountByDictType(String dictType) {
  20 + return selectCount("dict_type", dictType);
  21 + }
  22 +
  23 + default boolean selectExistsByUpdateTimeAfter(Date maxUpdateTime) {
  24 + return selectOne(new QueryWrapper<SysDictDataDO>().select("id")
  25 + .gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
  26 + }
  27 +
  28 +}
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.dal.mysql;
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/SysDictDataService.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.dict;
  2 +
  3 +import cn.iocoder.yudao.framework.dict.core.service.DictDataFrameworkService;
  4 +
  5 +/**
  6 + * 字典数据 Service 接口
  7 + *
  8 + * @author ruoyi
  9 + */
  10 +public interface SysDictDataService extends DictDataFrameworkService {
  11 +
  12 + /**
  13 + * 初始化字典数据的本地缓存
  14 + */
  15 + void initLocalCache();
  16 +
  17 +}
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dict/impl/SysDictDataServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.dict.impl;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  6 +import cn.iocoder.yudao.userserver.modules.system.convert.dict.SysDictDataConvert;
  7 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.dict.SysDictDataDO;
  8 +import cn.iocoder.yudao.userserver.modules.system.dal.mysql.dict.SysDictDataMapper;
  9 +import cn.iocoder.yudao.userserver.modules.system.dict.SysDictDataService;
  10 +import com.google.common.collect.ImmutableTable;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.springframework.scheduling.annotation.Scheduled;
  13 +import org.springframework.stereotype.Service;
  14 +
  15 +import javax.annotation.PostConstruct;
  16 +import javax.annotation.Resource;
  17 +import java.util.Comparator;
  18 +import java.util.Date;
  19 +import java.util.List;
  20 +
  21 +/**
  22 + * 字典数据 Service 实现类
  23 + *
  24 + * @author ruoyi
  25 + */
  26 +@Service
  27 +@Slf4j
  28 +public class SysDictDataServiceImpl implements SysDictDataService {
  29 +
  30 + /**
  31 + * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
  32 + * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
  33 + */
  34 + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
  35 +
  36 + /**
  37 + * 字典数据缓存,第二个 key 使用 label
  38 + *
  39 + * key1:字典类型 dictType
  40 + * key2:字典标签 label
  41 + */
  42 + private ImmutableTable<String, String, SysDictDataDO> labelDictDataCache;
  43 + /**
  44 + * 字典数据缓存,第二个 key 使用 value
  45 + *
  46 + * key1:字典类型 dictType
  47 + * key2:字典值 value
  48 + */
  49 + private ImmutableTable<String, String, SysDictDataDO> valueDictDataCache;
  50 + /**
  51 + * 缓存字典数据的最大更新时间,用于后续的增量轮询,判断是否有更新
  52 + */
  53 + private volatile Date maxUpdateTime;
  54 +
  55 + @Resource
  56 + private SysDictDataMapper dictDataMapper;
  57 +
  58 + @Override
  59 + @PostConstruct
  60 + public synchronized void initLocalCache() {
  61 + // 获取字典数据列表,如果有更新
  62 + List<SysDictDataDO> dataList = this.loadDictDataIfUpdate(maxUpdateTime);
  63 + if (CollUtil.isEmpty(dataList)) {
  64 + return;
  65 + }
  66 +
  67 + // 构建缓存
  68 + ImmutableTable.Builder<String, String, SysDictDataDO> labelDictDataBuilder = ImmutableTable.builder();
  69 + ImmutableTable.Builder<String, String, SysDictDataDO> valueDictDataBuilder = ImmutableTable.builder();
  70 + dataList.forEach(dictData -> {
  71 + labelDictDataBuilder.put(dictData.getDictType(), dictData.getLabel(), dictData);
  72 + valueDictDataBuilder.put(dictData.getDictType(), dictData.getValue(), dictData);
  73 + });
  74 + labelDictDataCache = labelDictDataBuilder.build();
  75 + valueDictDataCache = valueDictDataBuilder.build();
  76 + assert dataList.size() > 0; // 断言,避免告警
  77 + maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
  78 + log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
  79 + }
  80 +
  81 + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
  82 + public void schedulePeriodicRefresh() {
  83 + initLocalCache();
  84 + }
  85 +
  86 + /**
  87 + * 如果字典数据发生变化,从数据库中获取最新的全量字典数据。
  88 + * 如果未发生变化,则返回空
  89 + *
  90 + * @param maxUpdateTime 当前字典数据的最大更新时间
  91 + * @return 字典数据列表
  92 + */
  93 + private List<SysDictDataDO> loadDictDataIfUpdate(Date maxUpdateTime) {
  94 + // 第一步,判断是否要更新。
  95 + if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
  96 + log.info("[loadDictDataIfUpdate][首次加载全量字典数据]");
  97 + } else { // 判断数据库中是否有更新的字典数据
  98 + if (!dictDataMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
  99 + return null;
  100 + }
  101 + log.info("[loadDictDataIfUpdate][增量加载全量字典数据]");
  102 + }
  103 + // 第二步,如果有更新,则从数据库加载所有字典数据
  104 + return dictDataMapper.selectList();
  105 + }
  106 +
  107 + @Override
  108 + public DictDataRespDTO getDictDataFromCache(String type, String value) {
  109 + return SysDictDataConvert.INSTANCE.convert02(valueDictDataCache.get(type, value));
  110 + }
  111 +
  112 + @Override
  113 + public DictDataRespDTO parseDictDataFromCache(String type, String label) {
  114 + return SysDictDataConvert.INSTANCE.convert02(labelDictDataCache.get(type, label));
  115 + }
  116 +
  117 + @Override
  118 + public List<DictDataRespDTO> listDictDatasFromCache(String type) {
  119 + return SysDictDataConvert.INSTANCE.convertList03(labelDictDataCache.row(type).values());
  120 + }
  121 +
  122 +}