初始化数据字典

This commit is contained in:
YunaiV 2021-09-27 09:47:37 +08:00
parent 15e2c0945d
commit 0439a5505a
13 changed files with 249 additions and 10 deletions

View File

@ -19,7 +19,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.0.0</revision>
<revision>1.0.0-snapshot</revision>
<!-- Maven 相关 -->
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>

View File

@ -167,7 +167,7 @@ public class SysUserController {
@PreAuthorize("@ss.hasPermission('system:user:import')")
public CommonResult<SysUserImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
List<SysUserImportExcelVO> list = ExcelUtils.raed(file, SysUserImportExcelVO.class);
List<SysUserImportExcelVO> list = ExcelUtils.read(file, SysUserImportExcelVO.class);
return success(userService.importUsers(list, updateSupport));
}

View File

@ -14,7 +14,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.0.0</revision>
<revision>1.1.0-snapshot</revision>
<!-- 统一依赖管理 -->
<spring.boot.version>2.4.5</spring.boot.version>
<!-- Web 相关 -->

View File

@ -39,7 +39,7 @@ public class ExcelUtils {
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
}
public static <T> List<T> raed(MultipartFile file, Class<T> head) throws IOException {
public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
return EasyExcel.read(file.getInputStream(), head, null)
.autoCloseStream(false) // 不要自动关闭交给 Servlet 自己处理
.doReadAllSync();

View File

@ -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) {

View File

@ -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<DictDataRespDTO> convertList03(Collection<SysDictDataDO> list);
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.userserver.modules.system.convert;

View File

@ -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;
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.userserver.modules.system.dal.dataobject;

View File

@ -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<SysDictDataDO> {
default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) {
return selectOne(new QueryWrapper<SysDictDataDO>().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<SysDictDataDO>().select("id")
.gt("update_time", maxUpdateTime).last("LIMIT 1")) != null;
}
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.userserver.modules.system.dal.mysql;

View File

@ -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();
}

View File

@ -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<String, String, SysDictDataDO> labelDictDataCache;
/**
* 字典数据缓存第二个 key 使用 value
*
* key1字典类型 dictType
* key2字典值 value
*/
private ImmutableTable<String, String, SysDictDataDO> valueDictDataCache;
/**
* 缓存字典数据的最大更新时间用于后续的增量轮询判断是否有更新
*/
private volatile Date maxUpdateTime;
@Resource
private SysDictDataMapper dictDataMapper;
@Override
@PostConstruct
public synchronized void initLocalCache() {
// 获取字典数据列表如果有更新
List<SysDictDataDO> dataList = this.loadDictDataIfUpdate(maxUpdateTime);
if (CollUtil.isEmpty(dataList)) {
return;
}
// 构建缓存
ImmutableTable.Builder<String, String, SysDictDataDO> labelDictDataBuilder = ImmutableTable.builder();
ImmutableTable.Builder<String, String, SysDictDataDO> 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<SysDictDataDO> 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<DictDataRespDTO> listDictDatasFromCache(String type) {
return SysDictDataConvert.INSTANCE.convertList03(labelDictDataCache.row(type).values());
}
}