package com.vincent.rsf.server.system.service.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.vincent.rsf.common.utils.GsonUtils;
|
import com.vincent.rsf.framework.common.DateUtils;
|
import com.vincent.rsf.framework.common.R;
|
import com.vincent.rsf.framework.exception.CoolException;
|
import com.vincent.rsf.server.system.entity.Config;
|
import com.vincent.rsf.server.system.enums.ConfigType;
|
import com.vincent.rsf.server.system.enums.StatusType;
|
import com.vincent.rsf.server.common.service.RedisService;
|
import com.vincent.rsf.server.system.config.ConfigCacheProperties;
|
import com.vincent.rsf.server.system.mapper.ConfigMapper;
|
import com.vincent.rsf.server.system.service.ConfigService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.PostConstruct;
|
import java.util.Date;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.ConcurrentHashMap;
|
|
/**
|
* Created by vincent on 8/30/2024
|
*/
|
@Service("configService")
|
@Slf4j
|
public class ConfigServiceImpl extends ServiceImpl<ConfigMapper, Config> implements ConfigService {
|
|
/** 与 {@link RedisService#set(String, String, Object, Integer)} 的 flag 段一致:sys_config 非永久 key,见 {@link ConfigCacheProperties} */
|
private static final String REDIS_FLAG_SYS_CONFIG = "SYS_CONFIG";
|
|
public static final Map<String, Config> CONFIG_CACHE = new ConcurrentHashMap<>();
|
|
@Autowired(required = false)
|
private RedisService redisService;
|
@Autowired
|
private ConfigCacheProperties configCacheProperties;
|
|
@PostConstruct
|
public void init() {
|
try {
|
// List<Config> list = this.list(new LambdaQueryWrapper<Config>().eq(Config::getStatus, StatusType.ENABLE.val));
|
List<Config> list = safeList(new LambdaQueryWrapper<Config>().eq(Config::getStatus, StatusType.ENABLE.val));
|
for (Config config : list) {
|
CONFIG_CACHE.put(config.getFlag(), config);
|
}
|
} catch (Exception e) {
|
log.warn("配置缓存初始化失败,跳过配置表加载,后续按默认流程处理", e);
|
}
|
}
|
|
private boolean redisReady() {
|
return redisService != null && Boolean.TRUE.equals(redisService.initialize);
|
}
|
|
private static boolean isEffectiveConfig(Config c) {
|
return c != null && (c.getDeleted() == null || c.getDeleted() == 0);
|
}
|
|
private Config loadConfigFromDb(String flag) {
|
return getOne(new LambdaQueryWrapper<Config>()
|
.eq(Config::getFlag, flag)
|
.eq(Config::getDeleted, 0), false);
|
}
|
|
private Config tryRedisGetConfig(String flag) {
|
try {
|
return redisService.get(REDIS_FLAG_SYS_CONFIG, flag);
|
} catch (Exception e) {
|
log.debug("sys_config Redis get flag={}", flag, e);
|
return null;
|
}
|
}
|
|
private void tryRedisSetexConfig(String flag, Config loaded) {
|
try {
|
int ttl = Math.max(1, configCacheProperties.getRedisTtlSeconds());
|
redisService.set(REDIS_FLAG_SYS_CONFIG, flag, loaded, ttl);
|
} catch (Exception e) {
|
log.warn("sys_config Redis setex flag={}", flag, e);
|
}
|
}
|
|
@Override
|
public void evictSysConfigRedis(String flag) {
|
if (!redisReady() || StringUtils.isBlank(flag)) {
|
return;
|
}
|
try {
|
redisService.delete(REDIS_FLAG_SYS_CONFIG, flag);
|
} catch (Exception e) {
|
log.warn("sys_config Redis evict flag={}", flag, e);
|
}
|
}
|
|
@Override
|
public Config getCachedOrLoad(String flag) {
|
if (StringUtils.isBlank(flag)) {
|
return null;
|
}
|
if (redisReady()) {
|
Config fromRedis = tryRedisGetConfig(flag);
|
if (isEffectiveConfig(fromRedis)) {
|
CONFIG_CACHE.put(flag, fromRedis);
|
return fromRedis;
|
}
|
Config loaded = loadConfigFromDb(flag);
|
if (loaded != null) {
|
CONFIG_CACHE.put(flag, loaded);
|
tryRedisSetexConfig(flag, loaded);
|
}
|
return loaded;
|
}
|
Config cached = CONFIG_CACHE.get(flag);
|
if (isEffectiveConfig(cached)) {
|
return cached;
|
}
|
if (cached != null) {
|
CONFIG_CACHE.remove(flag);
|
}
|
Config loaded = loadConfigFromDb(flag);
|
if (loaded != null) {
|
CONFIG_CACHE.put(flag, loaded);
|
}
|
return loaded;
|
}
|
|
@Override
|
@SuppressWarnings("unchecked")
|
public <T> T getVal(String key, Class<T> clazz) {
|
Config config = CONFIG_CACHE.get(key);
|
if (config == null) {
|
// List<Config> list = this.list(new LambdaQueryWrapper<Config>().eq(Config::getFlag, key));
|
List<Config> list = safeList(new LambdaQueryWrapper<Config>().eq(Config::getFlag, key));
|
config = list.stream().findFirst().orElse(null);
|
if (null == config) {
|
return null;
|
}
|
}
|
String val = config.getVal();
|
switch (ConfigType.query(config.getType())) {
|
case BOOLEAN:
|
if (val.equals("1") || val.trim().equalsIgnoreCase("TRUE")) {
|
return (T) Boolean.TRUE;
|
}
|
return (T) Boolean.FALSE;
|
case NUMBER:
|
if (clazz == Integer.class) {
|
return (T) Integer.valueOf(val);
|
} else if (clazz == Short.class) {
|
return (T) Short.valueOf(val);
|
} else if (clazz == Long.class) {
|
return (T) Long.valueOf(val);
|
} else if (clazz == Double.class) {
|
return (T) Double.valueOf(val);
|
}
|
throw new UnsupportedOperationException("Unsupported type: " + clazz.getName());
|
case STRING:
|
return (T) val;
|
case JSON:
|
return GsonUtils.fromJson(val, clazz);
|
case DATE:
|
return (T) DateUtils.convert(val);
|
default:
|
return null;
|
}
|
}
|
|
@Override
|
public <T> boolean setVal(String key, T val) {
|
Config config = CONFIG_CACHE.get(key);
|
if (config == null) {
|
// List<Config> list = this.list(new LambdaQueryWrapper<Config>().eq(Config::getFlag, key));
|
List<Config> list = safeList(new LambdaQueryWrapper<Config>().eq(Config::getFlag, key));
|
config = list.stream().findFirst().orElse(null);
|
if (null == config) {
|
return false;
|
}
|
}
|
|
ConfigType configType = ConfigType.query(config.getType());
|
switch (configType) {
|
case BOOLEAN:
|
if (!(val instanceof Boolean)) {
|
throw new IllegalArgumentException("Expected Boolean value for key: " + key);
|
}
|
config.setVal((Boolean) val ? "TRUE" : "FALSE");
|
break;
|
case NUMBER:
|
if (val instanceof Integer || val instanceof Short || val instanceof Long || val instanceof Double) {
|
config.setVal(String.valueOf(val));
|
} else {
|
throw new IllegalArgumentException("Expected a numeric value for key: " + key);
|
}
|
break;
|
case STRING:
|
if (!(val instanceof String)) {
|
throw new IllegalArgumentException("Expected a String value for key: " + key);
|
}
|
config.setVal((String) val);
|
break;
|
case JSON:
|
config.setVal(GsonUtils.toJson(val));
|
break;
|
case DATE:
|
if (!(val instanceof Date)) {
|
throw new IllegalArgumentException("Expected a Date value for key: " + key);
|
}
|
config.setVal(DateUtils.convert((Date) val));
|
break;
|
default:
|
throw new UnsupportedOperationException("Unsupported ConfigType: " + configType);
|
}
|
|
boolean ok = this.updateById(config);
|
if (ok) {
|
evictSysConfigRedis(key);
|
}
|
return ok;
|
}
|
|
private List<Config> safeList(LambdaQueryWrapper<Config> wrapper) {
|
try {
|
return this.list(wrapper);
|
} catch (Exception e) {
|
log.warn("配置查询失败,跳过配置表读取,按默认流程处理", e);
|
return java.util.Collections.emptyList();
|
}
|
}
|
|
/**
|
* 修改配置
|
* @param config
|
* @return
|
*/
|
@Override
|
public R modiftyStatus(Config config) {
|
if (!this.update(new LambdaUpdateWrapper<Config>().set(Config::getVal, config.getVal()).eq(Config::getFlag, config.getFlag()))) {
|
throw new CoolException("修改失败!!");
|
}
|
Config fresh = getOne(new LambdaQueryWrapper<Config>()
|
.eq(Config::getFlag, config.getFlag())
|
.eq(Config::getDeleted, 0), false);
|
if (fresh != null) {
|
CONFIG_CACHE.put(fresh.getFlag(), fresh);
|
} else {
|
CONFIG_CACHE.remove(config.getFlag());
|
}
|
evictSysConfigRedis(config.getFlag());
|
return R.ok();
|
}
|
|
}
|