Merge pull request #10 from hiparker/development

Development
pull/12/head 1.6.2
ParkerZhou 4 years ago committed by GitHub
commit a04f65aef2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,7 +19,9 @@ package org.opsli.common.annotation;
import java.lang.annotation.*;
/**
* Api
*
*
*
*
* @author Parker
* @date 202151814:46:02
@ -27,15 +29,9 @@ import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiCryptoAsymmetric {
public @interface LoginCrypto {
/** 加密启用状态 */
boolean enable() default true;
/** 请求解密 */
boolean requestDecrypt() default true;
/** 返回加密 */
boolean responseEncrypt() default true;
}

@ -21,14 +21,16 @@ package org.opsli.common.constants;
* @author Parker
* @date 2020-09-22 17:07
*/
public interface CacheConstants {
public final class CacheConstants {
String PREFIX_NAME = "opsli";
public static final String PREFIX_NAME = "opsli";
/** Ehcache 缓存存放空间 */
String EHCACHE_SPACE = "timed";
public static final String EHCACHE_SPACE = "timed";
/** 热数据前缀 */
String HOT_DATA_PREFIX = "hot_data";
public static final String HOT_DATA_PREFIX = "hot_data";
private CacheConstants(){}
}

@ -1,35 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.common.constants;
/**
*
*
* @author Parker
* @date 2020-09-16 17:42
*/
public interface DictConstants {
/** 缓存前缀 NAME */
String CACHE_PREFIX_NAME = "dict:name:";
/** 缓存前缀 VALUE*/
String CACHE_PREFIX_VALUE = "dict:value:";
/** 缓存前缀 LIST*/
String CACHE_PREFIX_LIST = "dict:list:";
}

@ -21,18 +21,20 @@ package org.opsli.common.constants;
* @author Parker
* @date 202131015:50:16
*/
public interface MenuConstants {
public final class MenuConstants {
/** 菜单根节点ID */
String GEN_ID = "0";
public static final String GEN_ID = "0";
/** 菜单类型 */
String MENU = "1";
public static final String MENU = "1";
/** 按钮类型 */
String BUTTON = "2";
public static final String BUTTON = "2";
/** 外链类型 */
String EXTERNAL = "3";
public static final String EXTERNAL = "3";
private MenuConstants(){}
}

@ -23,35 +23,37 @@ package org.opsli.common.constants;
* @author Parker
* @date 2020-09-16 17:42
*/
public interface MyBatisConstants {
public final class MyBatisConstants {
/** 逻辑删除值 */
String LOGIC_DELETE_VALUE = "1";
public static final String LOGIC_DELETE_VALUE = "1";
/** 逻辑不删除值 */
String LOGIC_NOT_DELETE_VALUE = "0";
public static final String LOGIC_NOT_DELETE_VALUE = "0";
/** ID */
String FIELD_ID = "id";
public static final String FIELD_ID = "id";
/** PID */
String FIELD_PARENT_ID = "parentId";
public static final String FIELD_PARENT_ID = "parentId";
/** PIDs */
String FIELD_PARENT_IDS = "parentIds";
public static final String FIELD_PARENT_IDS = "parentIds";
/** 创建人 */
String FIELD_CREATE_BY = "createBy";
public static final String FIELD_CREATE_BY = "createBy";
/** 更新时间 */
String FIELD_CREATE_TIME = "createTime";
public static final String FIELD_CREATE_TIME = "createTime";
/** 更新人 */
String FIELD_UPDATE_BY = "updateBy";
public static final String FIELD_UPDATE_BY = "updateBy";
/** 更新时间 */
String FIELD_UPDATE_TIME = "updateTime";
public static final String FIELD_UPDATE_TIME = "updateTime";
/** 逻辑删除 */
String FIELD_DELETE_LOGIC = "deleted";
public static final String FIELD_DELETE_LOGIC = "deleted";
/** 乐观锁 */
String FIELD_OPTIMISTIC_LOCK = "version";
public static final String FIELD_OPTIMISTIC_LOCK = "version";
/** 多租户字段 */
String FIELD_TENANT = "tenantId";
public static final String FIELD_TENANT = "tenantId";
/** 组织字段 */
String FIELD_ORG_GROUP = "orgIds";
public static final String FIELD_ORG_GROUP = "orgIds";
private MyBatisConstants(){}
}

@ -21,36 +21,38 @@ package org.opsli.common.constants;
* @author Parker
* @date 2020-09-16 17:42
*/
public interface OrderConstants {
public final class OrderConstants {
/** Util 加载顺序 */
int UTIL_ORDER = 140;
public static final int UTIL_ORDER = 140;
/** 限流器 */
int LIMITER_AOP_SORT = 149;
public static final int LIMITER_AOP_SORT = 149;
/** token */
int TOKEN_AOP_SORT = 150;
public static final int TOKEN_AOP_SORT = 150;
/** 请求加解密 */
int ENCRYPT_ADN_DECRYPT_AOP_SORT = 160;
public static final int ENCRYPT_ADN_DECRYPT_AOP_SORT = 160;
/** 热点数据加载顺序 */
int HOT_DATA_ORDER = 180;
public static final int HOT_DATA_ORDER = 180;
/** 参数非法验证顺序 */
int VERIFY_ARGS_AOP_SORT = 185;
public static final int VERIFY_ARGS_AOP_SORT = 185;
/** 搜索历史 */
int SEARCH_HIS_AOP_SORT = 186;
public static final int SEARCH_HIS_AOP_SORT = 186;
/** SQL 切面执行顺序 */
int SQL_ORDER = 190;
public static final int SQL_ORDER = 190;
/** 参数非法验证顺序 */
int LOG_ORDER = 200;
public static final int LOG_ORDER = 200;
/** Controller异常拦截顺序 */
int EXCEPTION_HANDLER_ORDER = 260;
public static final int EXCEPTION_HANDLER_ORDER = 260;
private OrderConstants(){}
}

@ -0,0 +1,68 @@
package org.opsli.common.constants;
/**
* Redis
* {}
* @author
* @date 2021/12/10 19:52
*/
public final class RedisConstants {
/** 字典名称 */
public static final String PREFIX_DICT_NAME = "hash#{}:dict:name:";
/** 字典值 VALUE */
public static final String PREFIX_DICT_VALUE = "hash#{}:dict:value:";
/** 菜单编号 */
public static final String PREFIX_MENU_CODE = "kv#{}:menu:code:";
/** 参数编号 */
public static final String PREFIX_OPTIONS_CODE = "hash#{}:options";
/** 用户搜索记录 */
public static final String PREFIX_HIS_USERNAME = "zset#{}:his:username:";
/** 用户ID */
public static final String PREFIX_USER_ID = "kv#{}:user_id:";
/** 用户ID 和 角色 */
public static final String PREFIX_USER_ID_AND_ROLES = "kv#{}:user_id:roles:";
/** 用户ID 和 默认角色 */
public static final String PREFIX_USER_ID_DEF_ROLE = "kv#{}:user_id:def_role_id:";
/** 用户ID 和 组织 */
public static final String PREFIX_USER_ID_ORGS = "kv#{}:user_id:orgs:";
/** 用户ID 和 默认组织 */
public static final String PREFIX_USER_ID_DEF_ORG = "kv#{}:user_id:def_org:";
/** 用户ID 和 权限 */
public static final String PREFIX_USER_ID_PERMISSIONS = "kv#{}:user_id:permissions:";
/** 用户ID 和 菜单 */
public static final String PREFIX_USER_ID_MENUS = "kv#{}:user_id:menus:";
/** 用户名 */
public static final String PREFIX_USERNAME = "kv#{}:username:";
/** 票据 */
public static final String PREFIX_TICKET = "set#{}:ticket:";
/** 账号失败次数 */
public static final String PREFIX_ACCOUNT_SLIP_COUNT = "kv#{}:account:slip:count:";
/** 账号失败锁定KEY */
public static final String PREFIX_ACCOUNT_SLIP_LOCK = "kv#{}:account:slip:lock:";
private RedisConstants(){}
}

@ -6,24 +6,26 @@ package org.opsli.common.constants;
* @author Parker
* @date 2020-09-16 17:42
*/
public interface SignConstants {
public final class SignConstants {
/** 用户账号 */
String ACCOUNT = "account";
public static final String ACCOUNT = "account";
/** 用户ID */
String USER_ID = "userId";
public static final String USER_ID = "userId";
/** 租户ID */
String TENANT_ID = "tenantId";
public static final String TENANT_ID = "tenantId";
/** 时间戳 */
String TIMESTAMP = "timestamp";
public static final String TIMESTAMP = "timestamp";
/** 其他信息 */
String OTHER = "other";
public static final String OTHER = "other";
/** 签名 类型 */
String TYPE = "type";
public static final String TYPE = "type";
private SignConstants(){}
}

@ -21,9 +21,11 @@ package org.opsli.common.constants;
* @author Parker
* @date 2020-09-16 17:42
*/
public interface TokenConstants {
public final class TokenConstants {
/** ACCESS_TOKEN */
String ACCESS_TOKEN = "X-Token";
public static final String ACCESS_TOKEN = "X-Token";
private TokenConstants(){}
}

@ -6,12 +6,13 @@ package org.opsli.common.constants;
* @author Parker
* @date 2020-09-16 17:42
*/
public interface TokenTypeConstants {
public final class TokenTypeConstants {
/** 系统内部TOKEN */
String TYPE_SYSTEM = "system";
public static final String TYPE_SYSTEM = "system";
/** 外部TOKEN */
String TYPE_EXTERNAL = "external";
public static final String TYPE_EXTERNAL = "external";
private TokenTypeConstants(){}
}

@ -21,13 +21,15 @@ package org.opsli.common.constants;
* @author Parker
* @date 202131015:50:16
*/
public interface TreeConstants {
public final class TreeConstants {
/** 是否包含子集 */
String HAS_CHILDREN = "hasChildren";
public static final String HAS_CHILDREN = "hasChildren";
/** 是否是叶子节点 */
String IS_LEAF = "isLeaf";
public static final String IS_LEAF = "isLeaf";
private TreeConstants(){}
}

@ -1,72 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.autoconfigure.conf;
import lombok.extern.slf4j.Slf4j;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
/**
*
*
* @author Parker
* @date 2020-09-15
**/
@Slf4j
@Configuration
@ConditionalOnProperty(name = "spring.redis.pushsub.enable", havingValue = "true")
public class RedisMessageListenerConfig {
/**
* redis
* redis
*
*/
@Bean
public RedisMessageListenerContainer container(LettuceConnectionFactory lettuceConnectionFactory) {
RedisPushSubReceiver receiver = new RedisPushSubReceiver();
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(lettuceConnectionFactory);
//订阅了的通道
container.addMessageListener(listenerAdapter(new RedisPushSubReceiver()), new PatternTopic(receiver.getListenerChannel()));
return container;
}
/**
*
*
*
*/
@Bean
public MessageListenerAdapter listenerAdapter(RedisPushSubReceiver baseReceiver) {
//这个地方 是给messageListenerAdapter 传入一个消息接受的处理器利用反射的方法调用“receiveMessage”
//也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
//receiveMessage就是对应消费者那边的消费方法吗,而Receiver是自己弄的一个消费者类
return new MessageListenerAdapter(baseReceiver, "receiveMessage");
}
}

@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
@EqualsAndHashCode(callSuper = false)
public class CacheProperties {
public static final String PROP_PREFIX = "spring.cache-conf";
public static final String PROP_PREFIX = "spring.cache";
/** 缓存前缀 */
private String prefix;

@ -37,30 +37,24 @@ import org.opsli.api.base.warpper.ApiWrapper;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.annotation.RequiresPermissionsCus;
import org.opsli.common.annotation.hotdata.EnableHotData;
import org.opsli.common.constants.CacheConstants;
import org.opsli.common.constants.TreeConstants;
import org.opsli.common.enums.ExcelOperate;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.exception.TokenException;
import org.opsli.common.msg.CommonMsg;
import org.opsli.common.utils.OutputStreamUtil;
import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.base.entity.BaseEntity;
import org.opsli.core.base.entity.HasChildren;
import org.opsli.core.base.service.interfaces.CrudServiceInterface;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.msg.TokenMsg;
import org.opsli.core.security.shiro.realm.JwtRealm;
import org.opsli.core.utils.DistributedLockUtil;
import org.opsli.core.utils.ExcelUtil;
import org.opsli.core.utils.UserUtil;
import org.opsli.plugins.excel.exception.ExcelPluginException;
import org.opsli.plugins.excel.listener.BatchExcelListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -98,90 +92,6 @@ public abstract class BaseRestController <T extends BaseEntity, E extends ApiWra
@Autowired(required = false)
protected S IService;
/**
*
* id
*
* @param id id
* @return E
*/
@ModelAttribute
public E get(@RequestParam(required=false) String id) {
E model;
if (StringUtils.isNotBlank(id)){
// 如果开启缓存 先从缓存读
if(hotDataFlag){
// 缓存Key
String cacheKey = CacheConstants.HOT_DATA_PREFIX +":"+ id;
model = WrapperUtil.transformInstance(
CacheUtil.getTimed(entityClazz, cacheKey)
, modelClazz);
if(model != null){
return model;
}
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(!hasNilFlag) {
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
throw new ServiceException(CoreMsg.CACHE_PUNCTURE_EXCEPTION);
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
model = WrapperUtil.transformInstance(
CacheUtil.getTimed(entityClazz, cacheKey)
, modelClazz);
if(model != null){
return model;
}
// 如果缓存没读到 则去数据库读
model = WrapperUtil.transformInstance(IService.get(id), modelClazz);
// 获得数据后处理
if(model != null){
// 这里会 同步更新到本地Ehcache 和 Redis缓存
// 如果其他服务器缓存也丢失了 则 回去Redis拉取
CacheUtil.put(cacheKey, model);
return model;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(model == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
}
}
}else {
// 如果没开启缓存 则直接查询数据库
model = WrapperUtil.transformInstance(IService.get(id), modelClazz);
if(model != null){
return model;
}
}
}
// 如果参数没传入ID 则创建一个空的对象
try {
// 创建泛型对象
model = this.createModel();
}catch (Exception e){
log.error(CommonMsg.EXCEPTION_CONTROLLER_MODEL.toString()+" : {}",e.getMessage());
throw new ServiceException(CommonMsg.EXCEPTION_CONTROLLER_MODEL);
}
return model;
}
/**
* Excel
* @param request request

@ -0,0 +1,73 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.opsli.core.autoconfigure.properties.CacheProperties;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.utils.ThrowExceptionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
/**
*
*
* @author Parker
* @date 2020-09-16 16:20
*/
@Slf4j
@Order(UTIL_ORDER)
@Component
public class CacheUtil {
/** 热点数据前缀 */
private static String PREFIX_NAME;
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
/**
* Key
* @param key Key
* @return String
*/
public static String formatKey(String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return StrUtil.format(key, PREFIX_NAME);
}
/**
*
*/
@Autowired
public void init(CacheProperties cacheProperties){
CacheUtil.PREFIX_NAME = Convert.toStr(cacheProperties.getPrefix(), "opsli");
IS_INIT = true;
}
}

@ -0,0 +1,640 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.Striped;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
/**
*
*
* 使
*
* Redis String Hash
*
* @author Parker
* @date 2021/12/10 12:39
*/
@Slf4j
public final class SecurityCache {
/** 热点数据缓存时间 秒 (6小时)*/
private static final int TTL_HOT_DATA_TIME = 21600;
/** 默认缓存时效 超出后自动清理 (分钟) */
private static final int DEFAULT_CACHE_TIME = 5;
/** 创建缓存对象 用于存储 空缓存 */
private static final Cache<String, CacheStatus> LFU_NULL_CACHE;
/** 缓存前缀 KV */
private static final String CACHE_PREFIX_KV = "kv#";
/** 缓存前缀 HASH */
private static final String CACHE_PREFIX_HASH = "hash#";
/** 默认锁时间 (秒) */
private static final int DEFAULT_LOCK_TIME = 10;
/** 锁 */
@SuppressWarnings("UnstableApiUsage")
private static final Striped<Lock> STRIPED = Striped.lock(1024);
static {
// 超时自动清理
LFU_NULL_CACHE = CacheBuilder
.newBuilder()
.expireAfterWrite(DEFAULT_CACHE_TIME, TimeUnit.MINUTES).build();
}
/**
*
* @param redisTemplate redisTemplate
* @param key
* @param callbackSource
* @return Object
*/
public static Object get(
final RedisTemplate<String, Object> redisTemplate,
final String key, final Function<String, Object> callbackSource) {
return get(redisTemplate, key, callbackSource, false);
}
/**
*
* @param redisTemplate redisTemplate
* @param key
* @param callbackSource
* @return Object
*/
public static Object get(
final RedisTemplate<String, Object> redisTemplate,
final String key, final Function<String, Object> callbackSource, final boolean isEden){
if(null == redisTemplate || null == key || null == callbackSource){
throw new RuntimeException("入参[redisTemplate,key,callbackSource]必填");
}
// 缓存 Object 对象
Object cache = getCacheObject(redisTemplate, key);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果还没查到缓存 则需要 穿透到 源数据查询
// 开启本地锁
@SuppressWarnings("UnstableApiUsage")
Lock lock = STRIPED.get(key);
try {
// 尝试获得锁
if(lock.tryLock(DEFAULT_LOCK_TIME, TimeUnit.SECONDS)){
// 梅开二度 如果查到后 直接返回
cache = getCacheObject(redisTemplate, key);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果这时候还没有 则查询源数据
cache = callbackSource.apply(key);
if(null == cache){
// 存储缓存状态
LFU_NULL_CACHE.put(key, CacheStatus.NOT_EXIST);
return null;
}
// 存入 Redis缓存
put(redisTemplate, key, cache, isEden);
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
lock.unlock();
}
return cache;
}
/**
*
* @param redisTemplate redisTemplate
* @param key
* @param val
*/
public static void put(
final RedisTemplate<String, Object> redisTemplate,
final String key, final Object val) {
put(redisTemplate, key, val, false);
}
/**
*
* @param redisTemplate redisTemplate
* @param key
* @param val
* @param isEden
*/
public static void put(
final RedisTemplate<String, Object> redisTemplate,
final String key, final Object val, final boolean isEden) {
if (null == redisTemplate || null == key || null == val) {
throw new RuntimeException("入参[redisTemplate,key,val]必填");
}
String cacheKey = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_KV);
// 判断是否为永久存储
if(isEden) {
redisTemplate.opsForValue()
.set(cacheKey, val);
}else{
// 随机缓存失效时间 防止缓存雪崩
// 范围在当前时效的 1.2 - 2倍
// 生成随机失效时间
int timeout = RandomUtil.randomInt(
Convert.toInt(TTL_HOT_DATA_TIME * 1.2),
Convert.toInt(TTL_HOT_DATA_TIME * 2)
);
redisTemplate.opsForValue()
.set(
cacheKey,
val,
timeout,
TimeUnit.SECONDS
);
}
// 清除本地记录
LFU_NULL_CACHE.invalidate(key);
}
/**
* Hash
* @param redisTemplate redisTemplate
* @param key
* @param callbackSource
* @return Object
*/
public static Object hGet(
final RedisTemplate<String, Object> redisTemplate,
final String key, final String field, final Function<String, Object> callbackSource){
if(null == redisTemplate || null == key
|| null == field || null == callbackSource){
throw new RuntimeException("入参[redisTemplate,key,field,callbackSource]必填");
}
final String tempKey = key + "_" + field;
// 缓存 Object 对象
Object cache = getHashCacheObject(redisTemplate, key, field);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果还没查到缓存 则需要 穿透到 源数据查询
// 开启本地锁
@SuppressWarnings("UnstableApiUsage")
Lock lock = STRIPED.get(tempKey);
try {
// 尝试获得锁
if(lock.tryLock(DEFAULT_LOCK_TIME, TimeUnit.SECONDS)){
// 梅开二度 如果查到后 直接返回
cache = getHashCacheObject(redisTemplate, key, field);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果这时候还没有 则查询源数据
cache = callbackSource.apply(null);
if(null == cache){
// 存储缓存状态
LFU_NULL_CACHE.put(tempKey, CacheStatus.NOT_EXIST);
return null;
}
// 存入 Redis缓存
hPut(redisTemplate, key, field, cache);
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
lock.unlock();
}
return cache;
}
/**
* Hash
* hash
* redis redis
*
* @param redisTemplate redisTemplate
* @param key
* @param callbackSource
* @return Object
*/
public static Map<String, Object> hGetAll(
final RedisTemplate<String, Object> redisTemplate, final String key,
final Function<String, Map<String, Object>> callbackSource){
if(null == redisTemplate || null == key
|| null == callbackSource){
throw new RuntimeException("入参[redisTemplate,key,callbackSource]必填");
}
// 缓存 Object 对象
Map<String, Object> cache = getAllHashCacheObject(redisTemplate, key, null);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果还没查到缓存 则需要 穿透到 源数据查询
// 开启本地锁
@SuppressWarnings("UnstableApiUsage")
Lock lock = STRIPED.get(key);
try {
// 尝试获得锁
if(lock.tryLock(DEFAULT_LOCK_TIME, TimeUnit.SECONDS)){
// 梅开二度 如果查到后 直接返回
cache = getAllHashCacheObject(redisTemplate, key, null);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果这时候还没有 则查询源数据
cache = callbackSource.apply(null);
if(null == cache){
// 存储缓存状态
LFU_NULL_CACHE.put(key, CacheStatus.NOT_EXIST);
return null;
}
// 存入 Redis缓存
hAllPut(redisTemplate, key, cache);
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
lock.unlock();
}
return cache;
}
/**
* Hash
*
* @param redisTemplate redisTemplate
* @param key
* @param callbackSourceCount
* @param callbackSource
* @return Object
*/
public static Map<String, Object> hGetAll(
final RedisTemplate<String, Object> redisTemplate, final String key,
final Function<String, Integer> callbackSourceCount, final Function<String, Map<String, Object>> callbackSource){
if(null == redisTemplate || null == key
|| null == callbackSourceCount || null == callbackSource){
throw new RuntimeException("入参[redisTemplate,key,callbackSourceCount,callbackSource]必填");
}
// 缓存 Object 对象
Map<String, Object> cache = getAllHashCacheObject(redisTemplate, key, callbackSourceCount);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果还没查到缓存 则需要 穿透到 源数据查询
// 开启本地锁
@SuppressWarnings("UnstableApiUsage")
Lock lock = STRIPED.get(key);
try {
// 尝试获得锁
if(lock.tryLock(DEFAULT_LOCK_TIME, TimeUnit.SECONDS)){
// 梅开二度 如果查到后 直接返回
cache = getAllHashCacheObject(redisTemplate, key, callbackSourceCount);
// 如果缓存不为空 则直接返回
if(null != cache){
return cache;
}
// 如果这时候还没有 则查询源数据
cache = callbackSource.apply(null);
if(null == cache){
// 存储缓存状态
LFU_NULL_CACHE.put(key, CacheStatus.NOT_EXIST);
return null;
}
// 存入 Redis缓存
hAllPut(redisTemplate, key, cache);
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
lock.unlock();
}
return cache;
}
/**
* Hash
* @param redisTemplate redisTemplate
* @param key
* @param cacheMap Map
*/
public static void hAllPut(
final RedisTemplate<String, Object> redisTemplate,
final String key, final Map<String, Object> cacheMap) {
if (null == redisTemplate || null == key
|| null == cacheMap) {
throw new RuntimeException("入参[redisTemplate,key,cacheMap]必填");
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
redisTemplate.opsForHash()
.putAll(cacheKeyByHash, cacheMap);
// 清除本地记录
LFU_NULL_CACHE.invalidate(key);
}
/**
* Hash
* @param redisTemplate redisTemplate
* @param key
* @param field
* @param val
*/
public static void hPut(
final RedisTemplate<String, Object> redisTemplate,
final String key, final String field, final Object val) {
if (null == redisTemplate || null == key
|| null == field || null == val) {
throw new RuntimeException("入参[redisTemplate,key,field,val]必填");
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
redisTemplate.opsForHash()
.put(cacheKeyByHash, field, val);
final String tempKey = key + "_" + field;
// 清除本地记录
LFU_NULL_CACHE.invalidate(tempKey);
}
/**
* Hash
* @param redisTemplate redisTemplate
* @param key
* @param fields
* @return boolean
*/
public static boolean hDelMore(
final RedisTemplate<String, Object> redisTemplate,
final String key, final String... fields) {
if (null == redisTemplate || null == key
|| null == fields ) {
throw new RuntimeException("入参[redisTemplate,key,fields]必填");
}
int count = fields.length;
for (String field : fields) {
boolean isDel = hDel(redisTemplate, key, field);
if(isDel){
count--;
}
}
return 0 == count;
}
/**
* Hash
* @param redisTemplate redisTemplate
* @param key
* @param field
* @return boolean
*/
public static boolean hDel(
final RedisTemplate<String, Object> redisTemplate,
final String key, final String field) {
if (null == redisTemplate || null == key
|| null == field ) {
throw new RuntimeException("入参[redisTemplate,key,field]必填");
}
final String tempKey = key + "_" + field;
// 清除本地记录
LFU_NULL_CACHE.invalidate(tempKey);
// 判断是否存在
boolean isExist = Boolean.TRUE.equals(redisTemplate.opsForHash().hasKey(key, field));
if(!isExist){
// 如果不存在 直接返回删除成功
return true;
}
// 清除缓存
return 0 != redisTemplate.opsForHash().delete(key, field);
}
/**
*
* @param redisTemplate redisTemplate
* @param keys
*/
public static boolean removeMore(
final RedisTemplate<String, Object> redisTemplate,
final String... keys) {
if (null == redisTemplate || null == keys) {
throw new RuntimeException("入参[redisTemplate,keys]必填");
}
int count = keys.length;
for (String key : keys) {
boolean isRemove = remove(redisTemplate, key);
if(isRemove){
count--;
}
}
return 0 == count;
}
/**
*
* @param redisTemplate redisTemplate
* @param key
*/
public static boolean remove(
final RedisTemplate<String, Object> redisTemplate,
final String key) {
if (null == redisTemplate || null == key) {
throw new RuntimeException("入参[redisTemplate,key]必填");
}
// 清除本地记录
LFU_NULL_CACHE.invalidate(key);
String cacheKeyByKv = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_KV);
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
// 清除缓存
redisTemplate.delete(cacheKeyByKv);
redisTemplate.delete(cacheKeyByHash);
return true;
}
// =================================================================================================================
/**
* Redis
* @param redisTemplate redisTemplate
* @param key Key
* @return Object
*/
private static Object getCacheObject(RedisTemplate<String, Object> redisTemplate, String key) {
Object cache = null;
try {
// 先判断本地缓存是否存在 默认为存在(类似于伪布隆过滤)
CacheStatus cacheStatus = LFU_NULL_CACHE.get(key, () -> CacheStatus.EXIST);
// 如果不存在 则直接返回空
if(CacheStatus.NOT_EXIST.equals(cacheStatus)){
return null;
}
String cacheKey = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_KV);
// 从 缓存回调查询数据
cache = redisTemplate.opsForValue().get(cacheKey);
}catch (Exception e){
log.error(e.getMessage(), e);
}
return cache;
}
/**
* Redis Hash
* @param redisTemplate redisTemplate
* @param key Key
* @return Object
*/
private static Object getHashCacheObject(RedisTemplate<String, Object> redisTemplate, String key, String field) {
Object cache = null;
try {
final String tempKey = key + "_" + field;
// 先判断本地缓存是否存在 默认为存在(类似于伪布隆过滤)
CacheStatus cacheStatus = LFU_NULL_CACHE.get(tempKey, () -> CacheStatus.EXIST);
// 如果不存在 则直接返回空
if(CacheStatus.NOT_EXIST.equals(cacheStatus)){
return null;
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
// 从 缓存回调查询数据
cache = redisTemplate.opsForHash().get(cacheKeyByHash, field);
}catch (Exception e){
log.error(e.getMessage(), e);
}
return cache;
}
/**
* Redis Hash
* @param redisTemplate redisTemplate
* @param key key
* @param callbackSourceCount callbackSourceCount
* @return Object
*/
private static Map<String, Object> getAllHashCacheObject(RedisTemplate<String, Object> redisTemplate, String key,
final Function<String, Integer> callbackSourceCount) {
Map<String, Object> cache = null;
try {
// 先判断本地缓存是否存在 默认为存在(类似于伪布隆过滤)
CacheStatus cacheStatus = LFU_NULL_CACHE.get(key, () -> CacheStatus.EXIST);
// 如果不存在 则直接返回空
if(CacheStatus.NOT_EXIST.equals(cacheStatus)){
return null;
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
// 从 缓存回调查询数据
Map<Object, Object> entries = redisTemplate.opsForHash().entries(cacheKeyByHash);
// 如果补偿器不为空 则进行补偿判断
if(null != callbackSourceCount){
// 判断数量不相等 则返回一个空 重新查询处理缓存
Integer count = callbackSourceCount.apply(key);
if(count != entries.size()){
return null;
}
}
if(MapUtil.isNotEmpty(entries)){
cache = new HashMap<>();
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
cache.put(String.valueOf(entry.getKey()), entry.getValue());
}
}
}catch (Exception e){
log.error(e.getMessage(), e);
}
return cache;
}
/**
*
*/
private enum CacheStatus{
/** 存在(默认) */
EXIST,
/** 不存在 */
NOT_EXIST
}
/**
*
*/
private SecurityCache(){}
}

@ -1,813 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.local;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.XmlUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.opsli.common.constants.CacheConstants;
import org.opsli.common.enums.CacheType;
import org.opsli.core.autoconfigure.properties.CacheProperties;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.utils.ThrowExceptionUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.opsli.plugins.redis.RedisPlugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
/**
*
*
* keyRedis
* 使expire()idletime
*
* bigkey()
* string10KBhashlistsetzset5000
*
* @author Parker
* @date 2020-09-16 16:20
*/
@Slf4j
@Order(UTIL_ORDER)
@Component
public class CacheUtil {
/** 热点数据缓存时间 秒 (6小时)*/
private static int TTL_HOT_DATA_TIME = 21600;
/** 空缓存时间 秒 */
private final static int TTL_NIL_DATA_TIME = 300;
/** Redis插件 */
private static RedisPlugin redisPlugin;
/** EhCache插件 */
private static EhCachePlugin ehCachePlugin;
/** Json key */
public static final String JSON_KEY = "data";
/** 空状态 key 前缀 */
private static final String NIL_FLAG_PREFIX = "nil";
/** 空状态 生效阈值 */
private final static long NIL_FLAG_THRESHOLD = 3;
/** 热点数据前缀 */
private static String PREFIX_NAME;
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
static {
try {
// 读取配置信息
CacheUtil.readPropertyXML();
}catch (Exception ignored){}
}
/***
*
* @return String
*/
public static String getPrefixName() {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return PREFIX_NAME;
}
// ========================= GET =========================
/**
*
* @param key
* @return Object
*/
public static Object getTimed(final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, false, false);
}
/**
*
* @param key
* @param isSaveLocal
* @return Object
*/
public static Object getTimed(final String key, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, false, isSaveLocal);
}
/**
*
* @param vClass Class
* @param key
* @return <V>
*/
public static <V> V getTimed(final Class<V> vClass, final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, false, false);
}
/**
*
* @param vClass Class
* @param key
* @param isSaveLocal
* @return <V>
*/
public static <V> V getTimed(final Class<V> vClass, final String key,
final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, false, isSaveLocal);
}
/**
*
* @param key
* @return Object
*/
public static Object getEden(final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, true, false);
}
/**
*
* @param key
* @param isSaveLocal
* @return Object
*/
public static Object getEden(final String key, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, true, isSaveLocal);
}
/**
*
* @param vClass Class
* @param key
* @return <V>
*/
public static <V> V getEden(final String key, final Class<V> vClass){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, true, false);
}
/**
*
* @param vClass Class
* @param key
* @param isSaveLocal
* @return <V>
*/
public static <V> V getEden(final String key, final boolean isSaveLocal,
final Class<V> vClass){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, true, isSaveLocal);
}
/**
*
* @param vClass Class
* @param key
* @param isEden
* @param isSaveLocal
* @return <V>
*/
private static <V> V get(final Class<V> vClass, final String key, final boolean isEden,
final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 获得缓存数据
Object cacheObj = CacheUtil.get(key, isEden, isSaveLocal);
// 转换数据泛型
return Convert.convert(vClass, cacheObj);
}
/**
*
* @param key
* @param isEden
* @param isSaveLocal
* @return Object
*/
private static Object get(final String key, final boolean isEden, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 缓存 Key
String cacheKey = CacheUtil.handleUsualKey(key, isEden);
// 获得缓存Json
JSONObject cacheJson;
// 判读是否需要 先从本地缓存获取
if(isSaveLocal){
// 获得缓存Json
cacheJson = ehCachePlugin.get(CacheConstants.EHCACHE_SPACE,
cacheKey, JSONObject.class);
if(cacheJson != null){
return cacheJson.get(JSON_KEY);
}
}
// 如果本地缓存找不到该缓存 则去远端缓存拉去缓存
cacheJson = (JSONObject) redisPlugin.get(cacheKey);
if(cacheJson != null){
// 判读是否需要 存入本地EhCache
if(isSaveLocal){
//存入EhCache
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE,
cacheKey, cacheJson);
}
}
return cacheJson != null ? cacheJson.get(JSON_KEY) : null;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
/**
* Hash
* @param vClass Class
* @param key
* @param field
* @return <V>
*/
public static <V> V getHash(final Class<V> vClass, final String key, final String field){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 获得缓存数据
Object cacheObj = CacheUtil.getHash(key, field, false);
// 转换数据泛型
return Convert.convert(vClass, cacheObj);
}
/**
* Hash
* @param key
* @param field
* @param isSaveLocal
* @param vClass Class
* @return <V>
*/
public static <V> V getHash(final Class<V> vClass, final String key,
final String field, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 获得缓存数据
Object cacheObj = CacheUtil.getHash(key, field, isSaveLocal);
// 转换数据泛型
return Convert.convert(vClass, cacheObj);
}
/**
* Hash
* @param key
* @param field
* @return Object
*/
public static Object getHash(final String key, final String field){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return CacheUtil.getHash(key, field, false);
}
/**
* Hash
* @param key
* @param field
* @param isSaveLocal
* @return Object
*/
public static Object getHash(final String key, final String field,
final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 缓存 Key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
// 获得缓存Json
JSONObject cacheJson;
// 判读是否需要 先从本地缓存获取
if(isSaveLocal){
// 获得缓存Json
cacheJson = ehCachePlugin.get(CacheConstants.EHCACHE_SPACE,
cacheKey +":"+ field, JSONObject.class);
if(cacheJson != null){
return cacheJson.get(JSON_KEY);
}
}
// 如果本地缓存找不到该缓存 则去远端缓存拉去缓存
cacheJson = (JSONObject) redisPlugin.hGet(cacheKey, field);
if(cacheJson != null){
// 判读是否需要 存入本地EhCache
if(isSaveLocal){
//存入EhCache
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE,
cacheKey + ":" + field, cacheJson);
}
}
return cacheJson != null ? cacheJson.get(JSON_KEY) : null;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
/**
* Hash
* @param key
* @return Object
*/
public static Map<String, Object> getHashAll(final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 缓存 Key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
Map<String, Object> retMap = Maps.newHashMap();
// 如果本地缓存找不到该缓存 则去远端缓存拉去缓存
Map<Object, Object> allCache = redisPlugin.hGetAll(cacheKey);
if(CollUtil.isEmpty(allCache)){
return retMap;
}
for (Map.Entry<Object, Object> entry : allCache.entrySet()) {
// 赋值
JSONObject jsonObject = (JSONObject) entry.getValue();
if (jsonObject == null) {
continue;
}
Object data = jsonObject.get(CacheUtil.JSON_KEY);
if (data == null) {
continue;
}
retMap.put(Convert.toStr(entry.getKey()), data);
}
return retMap;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
// ========================= PUT =========================
/**
*
* @param key
* @param value
* @return boolean
*/
public static boolean put(final String key, final Object value) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return CacheUtil.put(key, value, false);
}
/**
*
* @param key
* @param value
* @param isEden
* @return boolean
*/
public static boolean put(final String key, final Object value, final boolean isEden) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 自动处理 key
// 则统一转换为 JSONObject
JSONObject cacheJson = new JSONObject();
cacheJson.put(JSON_KEY, value);
// 缓存 Key
String cacheKey = CacheUtil.handleUsualKey(key, isEden);
// 判断是否为永久存储
if(isEden) {
// 存入Redis
return redisPlugin.put(cacheKey, cacheJson);
}else{
// 随机缓存失效时间 防止缓存雪崩
// 范围在当前时效的 1.2 - 2倍
// 生成随机失效时间
int timeout = RandomUtil.randomInt(
Convert.toInt(TTL_HOT_DATA_TIME * 1.2),
Convert.toInt(TTL_HOT_DATA_TIME * 2)
);
// 存入Redis
return redisPlugin.put(cacheKey, cacheJson, timeout);
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* Hash
* @param key
* @param field
* @param value
* @return boolean
*/
public static boolean putHash(final String key, final String field, final Object value) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 处理 key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
// 则统一转换为 JSONObject
JSONObject cacheJson = new JSONObject();
cacheJson.put(JSON_KEY, value);
// 存入Redis
return redisPlugin.hPut(cacheKey, field, cacheJson);
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
// ========================= DEL =========================
/**
*
* @param key
* @return boolean
*/
public static boolean del(final String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 计数器
int count = 0;
Object timed = CacheUtil.getTimed(key);
Object eden = CacheUtil.getEden(key);
// 删除key 集合
List<String> cacheKeys = Lists.newArrayList();
if(timed != null){
count+=2;
// 处理 key - 时控数据
cacheKeys.add(
CacheUtil.handleKey(CacheType.TIMED, key)
);
}
if(eden != null){
count+=2;
// 处理 key - 永久数据
cacheKeys.add(
CacheUtil.handleKey(CacheType.EDEN, key));
}
// 循环删除缓存数据
for (String cacheKey : cacheKeys) {
// 删除 EhCache
boolean ehcacheRet = ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
if(ehcacheRet){
count--;
}
// 删除 Redis
boolean redisRet = redisPlugin.del(cacheKey);
if(redisRet){
count--;
}
}
return count == 0;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* Hash
* @param key
* @param field
* @return boolean
*/
public static boolean delHash(final String key, final String field) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 计数器
int count = 2;
// 自动处理 key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
// 删除 EhCache
boolean ehcacheRet = ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE,cacheKey +":"+ field);
if(ehcacheRet){
count--;
}
// 删除 Redis
Long hDeleteLong = redisPlugin.hDelete(cacheKey, field);
if(hDeleteLong != null && hDeleteLong > 0){
count--;
}
return count == 0;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
// ====================================================================
/**
* 5
* 穿
*
* @param key
* @return boolean
*/
public static boolean putNilFlag(String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 处理缓存 key
String cacheKey = CacheUtil.handleKey(NIL_FLAG_PREFIX + ":" + key);
try {
// 存入Redis
Long increment = redisPlugin.increment(cacheKey);
// 设置失效时间
redisPlugin.expire(cacheKey, TTL_NIL_DATA_TIME);
return increment != null;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
*
* 穿
*
* @param key
* @return boolean
*/
public static boolean delNilFlag(String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 处理缓存 key
String cacheKey = CacheUtil.handleKey(NIL_FLAG_PREFIX + ":" + key);
try {
// 删除Redis
return redisPlugin.del(cacheKey);
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* 5
* 穿
*
* @param key
* @return boolean
*/
public static boolean hasNilFlag(String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 处理缓存 key
String cacheKey = CacheUtil.handleKey(NIL_FLAG_PREFIX + ":" + key);
try {
Object nilObj = redisPlugin.get(cacheKey);
if(nilObj == null){
return false;
}
Long nilNum = Convert.toLong(nilObj, 0L);
return NIL_FLAG_THRESHOLD < nilNum;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
// ====================================================================
/**
* key
* @param key Key
* @return String
*/
public static String handleKey(String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return CacheUtil.handleKey(CacheType.TIMED, key);
}
/**
* key
* @param cacheType
* @param key Key
* @return String
*/
public static String handleKey(CacheType cacheType, String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return PREFIX_NAME + cacheType.getName() + ":" +
key;
}
/**
* key
* @param key Key
* @param isEden
* @return String
*/
private static String handleUsualKey(String key, boolean isEden){
if(isEden){
return CacheUtil.handleKey(CacheType.EDEN, key);
}
return CacheUtil.handleKey(CacheType.TIMED, key);
}
/**
*
*/
private static void readPropertyXML() throws IOException {
// 有坑 读 xml
ClassPathResource resource = new ClassPathResource("config/ehcache-opsli.xml");
Document document = XmlUtil.readXML(resource.getInputStream());
NodeList nodeList = document.getElementsByTagName("cache");
if(nodeList != null){
for (int i = 0; i < nodeList.getLength(); i++) {
Node item = nodeList.item(i);
NamedNodeMap attributes = item.getAttributes();
if(attributes == null){
continue;
}
Node alias = attributes.getNamedItem("alias");
if("hotData".equals(alias.getNodeValue())){
NodeList childNodes = item.getChildNodes();
if(childNodes != null){
for (int j = 0; j < childNodes.getLength(); j++) {
if("expiry".equals(childNodes.item(j).getNodeName())){
NodeList expiryNodes = childNodes.item(j).getChildNodes();
if(expiryNodes != null){
for (int k = 0; k < expiryNodes.getLength(); k++) {
if("ttl".equals(expiryNodes.item(k).getNodeName())){
Node ttlNode = expiryNodes.item(k);
Node ttlValue = ttlNode.getFirstChild();
// 默认 60000秒 6小时
TTL_HOT_DATA_TIME = Convert.toInt(ttlValue.getNodeValue(), 21600);
break;
}
}
}
break;
}
}
}
break;
}
}
}
}
/**
*
*/
@Autowired
public void init(CacheProperties cacheProperties,
RedisPlugin redisPlugin,
EhCachePlugin ehCachePlugin){
CacheUtil.PREFIX_NAME = Convert.toStr(cacheProperties.getPrefix(), "opsli") + ":";
CacheUtil.redisPlugin = redisPlugin;
CacheUtil.ehCachePlugin = ehCachePlugin;
IS_INIT = true;
}
}

@ -1,38 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.opsli.core.cache.pushsub.enums.PushSubType;
/**
* Entity
*
* @author Parker
* @date 2020-09-18 00:01
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CacheDataEntity {
/** key */
private String key;
}

@ -1,34 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.enums;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
public enum CacheHandleType {
/** 更新 */
UPDATE,
/** 删除 */
DELETE,
;
}

@ -1,34 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.enums;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
public enum DictModelType {
/** 对象 */
OBJECT,
/** 集合 */
COLLECTION,
;
}

@ -1,67 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.enums;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
public enum MsgArgsType {
/** 字典模型 */
DICT_MODEL,
/** 字典模型-集合 */
DICT_MODELS,
/** 字典模型-传输类型 */
DICT_MODEL_TYPE,
/** 字典操作类型 */
DICT_TYPE,
/** 用户ID */
USER_ID,
/** 用户名 */
USER_USERNAME,
/** 用户数据类型 */
USER_MODEL_TYPE,
/** 用户数据*/
USER_MODEL_DATA,
/** 组织 用户ID */
ORG_USER_ID,
/** 组织 用户数据 */
ORG_USER_DATA,
/** 租户ID */
TENANT_ID,
/** 租户数据 */
TENANT_DATA,
/** 参数编号 */
OPTION_CODE,
/** 参数数据*/
OPTION_MODEL_DATA,
/** 缓存数据Key */
CACHE_DATA_KEY,
/** 缓存数据Value */
CACHE_DATA_VALUE,
/** 缓存数据Type */
CACHE_DATA_TYPE,
;
}

@ -1,50 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.enums;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
public enum PushSubType {
/** 字典类型 */
DICT,
/** 用户数据 */
USER,
/** 菜单数据 */
MENU,
/** 组织数据 */
ORG,
/** 租户 */
TENANT,
/** 系统数据 */
OPTION,
/** 热点数据 */
HOT_DATA,
;
}

@ -1,37 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.enums;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
public enum UserModelType {
/** 用户模型 */
USER_MODEL,
/** 用户角色集合模型 */
USER_ROLES_MODEL,
/** 用户权限集合模型 */
USER_PERMS_MODEL,
/** 用户菜单集合模型 */
USER_MENU_MODEL,
;
}

@ -1,109 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.opsli.api.wrapper.system.dict.DictWrapper;
import org.opsli.common.constants.CacheConstants;
import org.opsli.common.constants.DictConstants;
import org.opsli.common.enums.CacheType;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.enums.CacheHandleType;
import org.opsli.core.cache.pushsub.enums.DictModelType;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
@Slf4j
public class DictHandler implements RedisPushSubHandler{
@Autowired
private EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.DICT;
}
@Override
public void handler(JSONObject msgJson) {
DictModelType dictModelType = DictModelType.valueOf((String) msgJson.get(MsgArgsType.DICT_MODEL_TYPE.toString()));
CacheHandleType type = CacheHandleType.valueOf((String) msgJson.get(MsgArgsType.DICT_TYPE.toString()));
if(DictModelType.COLLECTION == dictModelType){
Object dictListObj = msgJson.get(MsgArgsType.DICT_MODELS.toString());
List<DictWrapper> dictWrappers = Convert.toList(DictWrapper.class, dictListObj);
if(CollUtil.isNotEmpty(dictWrappers)){
for (DictWrapper dictWrapper : dictWrappers) {
this.handler(dictWrapper, type);
}
}
} else if(DictModelType.OBJECT == dictModelType){
JSONObject jsonObject = msgJson.getJSONObject(MsgArgsType.DICT_MODEL.toString());
if(jsonObject == null){
return;
}
DictWrapper dictWrapperModel = jsonObject.toJavaObject(DictWrapper.class);
this.handler(dictWrapperModel, type);
}
}
/**
* -
* @param dictWrapperModel model
* @param type
*/
private void handler(DictWrapper dictWrapperModel, CacheHandleType type){
// 解析 key
String ehKeyByName = CacheUtil.handleKey(CacheType.EDEN_HASH, DictConstants.CACHE_PREFIX_NAME +
dictWrapperModel.getTypeCode() + ":" + dictWrapperModel.getDictName());
String ehKeyByValue = CacheUtil.handleKey(CacheType.EDEN_HASH, DictConstants.CACHE_PREFIX_VALUE +
dictWrapperModel.getTypeCode() + ":" + dictWrapperModel.getDictValue());
// 缓存更新
if(CacheHandleType.UPDATE == type){
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, ehKeyByName);
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, ehKeyByValue);
// 统一转换为 JSONObject
String jsonStr = JSONObject.toJSONString(dictWrapperModel.getModel());
JSONObject value = JSONObject.parseObject(jsonStr);
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE, ehKeyByName, value);
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE, ehKeyByValue, value);
}
// 缓存删除
else if(CacheHandleType.DELETE == type){
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, ehKeyByName);
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, ehKeyByValue);
}
}
}

@ -1,68 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.enums.CacheHandleType;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
@Slf4j
public class HotDataHandler implements RedisPushSubHandler{
@Autowired
private EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.HOT_DATA;
}
@Override
public void handler(JSONObject msgJson) {
String key = (String) msgJson.get(MsgArgsType.CACHE_DATA_KEY.toString());
Object value = msgJson.get(MsgArgsType.CACHE_DATA_VALUE.toString());
CacheHandleType type = CacheHandleType.valueOf((String )msgJson.get(MsgArgsType.CACHE_DATA_TYPE.toString()));
if(StringUtils.isEmpty(key)){
return;
}
// 拼装缓存key
String cacheName = CacheUtil.handleKey(CacheConstants.HOT_DATA_PREFIX +":"+ key);
if(CacheHandleType.UPDATE == type){
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE, cacheName, value);
}
// 缓存删除
else if(CacheHandleType.DELETE == type){
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheName);
}
}
}

@ -1,76 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.utils.OptionsUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
@Slf4j
public class OptionHandler implements RedisPushSubHandler{
@Autowired
private EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.OPTION;
}
@Override
public void handler(JSONObject msgJson) {
// 系统参数刷新
this.optionHandler(msgJson);
}
/**
*
* @param msgJson Json
*/
private void optionHandler(JSONObject msgJson){
JSONObject data = msgJson.getJSONObject(MsgArgsType.OPTION_MODEL_DATA.toString());
// 数据为空则不执行
if(data == null){
return;
}
// 获得参数编号
String optionCode = (String) msgJson.get(MsgArgsType.OPTION_CODE.toString());
if(StringUtils.isEmpty(optionCode)){
return;
}
String cacheKey = CacheUtil.handleKey(OptionsUtil.PREFIX_CODE + optionCode);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
}
}

@ -1,77 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.utils.OrgUtil;
import org.opsli.core.utils.UserUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
@Slf4j
public class OrgHandler implements RedisPushSubHandler{
@Autowired
private EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.ORG;
}
@Override
public void handler(JSONObject msgJson) {
// 用户刷新
this.orgHandler(msgJson);
}
/**
*
* @param msgJson Json
*/
private void orgHandler(JSONObject msgJson){
JSONObject data = msgJson.getJSONObject(MsgArgsType.ORG_USER_DATA.toString());
// 数据为空则不执行
if(data == null){
return;
}
// 获得用户ID
String userId = (String) msgJson.get(MsgArgsType.ORG_USER_ID.toString());
if(StringUtils.isEmpty(userId)){
return;
}
String cacheKey = CacheUtil.handleKey(UserUtil.PREFIX_ID_ORGS + userId);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
}
}

@ -1,41 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import com.alibaba.fastjson.JSONObject;
import org.opsli.core.cache.pushsub.enums.PushSubType;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
public interface RedisPushSubHandler {
/**
*
* @return
*/
PushSubType getType();
/**
*
* @param msgJson Json
*/
void handler(JSONObject msgJson);
}

@ -1,76 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.utils.TenantUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
@Slf4j
public class TenantHandler implements RedisPushSubHandler{
@Autowired
private EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.TENANT;
}
@Override
public void handler(JSONObject msgJson) {
// 用户刷新
this.orgHandler(msgJson);
}
/**
*
* @param msgJson Json
*/
private void orgHandler(JSONObject msgJson){
JSONObject data = msgJson.getJSONObject(MsgArgsType.TENANT_DATA.toString());
// 数据为空则不执行
if(data == null){
return;
}
// 获得租户ID
String tenantId = (String) msgJson.get(MsgArgsType.TENANT_ID.toString());
if(StringUtils.isEmpty(tenantId)){
return;
}
String cacheKey = CacheUtil.handleKey(TenantUtil.PREFIX_CODE + tenantId);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
}
}

@ -1,168 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.handler;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.enums.UserModelType;
import org.opsli.core.utils.UserUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
*
* @author Parker
* @date 2020-09-16
*/
@Slf4j
public class UserHandler implements RedisPushSubHandler{
@Autowired
private EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.USER;
}
@Override
public void handler(JSONObject msgJson) {
UserModelType userModelType = UserModelType.valueOf((String)
msgJson.get(MsgArgsType.USER_MODEL_TYPE.toString()));
// 用户刷新
if(UserModelType.USER_MODEL == userModelType){
this.userHandler(msgJson);
}
// 用户角色刷新
else if(UserModelType.USER_ROLES_MODEL == userModelType){
this.userRolesHandler(msgJson);
}
// 用户权限刷新
else if(UserModelType.USER_PERMS_MODEL == userModelType){
this.userPermsHandler(msgJson);
}
// 用户菜单刷新
else if(UserModelType.USER_MENU_MODEL == userModelType){
this.userMenusHandler(msgJson);
}
}
/**
*
* @param msgJson Json
*/
private void userHandler(JSONObject msgJson){
JSONObject data = msgJson.getJSONObject(MsgArgsType.USER_MODEL_DATA.toString());
// 数据为空则不执行
if(data == null){
return;
}
// 获得用户ID 和 用户名
String userId = (String) msgJson.get(MsgArgsType.USER_ID.toString());
String username = (String) msgJson.get(MsgArgsType.USER_USERNAME.toString());
if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(username) ){
return;
}
String cacheKeyById = CacheUtil.handleKey(UserUtil.PREFIX_ID + userId);
String cacheKeyByName = CacheUtil.handleKey(UserUtil.PREFIX_USERNAME + username);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKeyById);
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKeyByName);
}
/**
*
* @param msgJson Json
*/
private void userRolesHandler(JSONObject msgJson){
JSONArray dataArray = msgJson.getJSONArray(MsgArgsType.USER_MODEL_DATA.toString());
// 数据为空则不执行
if(dataArray == null || dataArray.isEmpty()){
return;
}
// 获得用户ID
String userId = (String) msgJson.get(MsgArgsType.USER_ID.toString());
if(StringUtils.isEmpty(userId)){
return;
}
String cacheKey = CacheUtil.handleKey(UserUtil.PREFIX_ID_ROLES + userId);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
}
/**
*
* @param msgJson Json
*/
private void userPermsHandler(JSONObject msgJson){
JSONArray dataArray = msgJson.getJSONArray(MsgArgsType.USER_MODEL_DATA.toString());
// 数据为空则不执行
if(dataArray == null || dataArray.isEmpty()){
return;
}
// 获得用户ID
String userId = (String) msgJson.get(MsgArgsType.USER_ID.toString());
if(StringUtils.isEmpty(userId)){
return;
}
String cacheKey = CacheUtil.handleKey(UserUtil.PREFIX_ID_PERMISSIONS + userId);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
}
/**
*
* @param msgJson Json
*/
private void userMenusHandler(JSONObject msgJson){
JSONArray dataArray = msgJson.getJSONArray(MsgArgsType.USER_MODEL_DATA.toString());
// 数据为空则不执行
if(dataArray == null || dataArray.isEmpty()){
return;
}
// 获得用户ID
String userId = (String) msgJson.get(MsgArgsType.USER_ID.toString());
if(StringUtils.isEmpty(userId)){
return;
}
String cacheKey = CacheUtil.handleKey(UserUtil.PREFIX_ID_MENUS + userId);
// 先删除
ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
}
}

@ -1,64 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.msgs;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import org.opsli.core.cache.pushsub.entity.CacheDataEntity;
import org.opsli.core.cache.pushsub.enums.CacheHandleType;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
/**
*
*
* @author Parker
* @date 2020-09-15
*/
@Data
@Accessors(chain = true)
public final class CacheDataMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL + RedisPushSubReceiver.CHANNEL;
private CacheDataMsgFactory(){}
/**
*
* @param cacheDataEntity
* @param value
* @param cacheHandleType
* @return
*/
public static BaseSubMessage createMsg(CacheDataEntity cacheDataEntity, Object value,
CacheHandleType cacheHandleType){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.CACHE_DATA_KEY.toString(),cacheDataEntity.getKey());
jsonObj.put(MsgArgsType.CACHE_DATA_VALUE.toString(),value);
jsonObj.put(MsgArgsType.CACHE_DATA_TYPE.toString(), cacheHandleType.toString());
// 热点数据 - 系统数据
baseSubMessage.build(CHANNEL, PushSubType.HOT_DATA.toString(), jsonObj);
return baseSubMessage;
}
}

@ -1,83 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.msgs;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import org.opsli.api.wrapper.system.dict.DictWrapper;
import org.opsli.core.cache.pushsub.enums.CacheHandleType;
import org.opsli.core.cache.pushsub.enums.DictModelType;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
import java.util.List;
/**
*
*
* @author Parker
* @date 2020-09-15
*/
@Data
@Accessors(chain = true)
public final class DictMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL;
private DictMsgFactory(){}
/**
*
* @param dictWrapperModel
* @param cacheHandleType
* @return
*/
public static BaseSubMessage createMsg(DictWrapper dictWrapperModel, CacheHandleType cacheHandleType){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.DICT_MODEL.toString(), dictWrapperModel);
jsonObj.put(MsgArgsType.DICT_MODEL_TYPE.toString(), DictModelType.OBJECT);
jsonObj.put(MsgArgsType.DICT_TYPE.toString(), cacheHandleType.toString());
// DICT 字典
baseSubMessage.build(CHANNEL,PushSubType.DICT.toString(),jsonObj);
return baseSubMessage;
}
/**
*
* @param dictWrapperModels
* @param cacheHandleType
* @return
*/
public static BaseSubMessage createMsg(List<DictWrapper> dictWrapperModels, CacheHandleType cacheHandleType){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.DICT_MODELS.toString(), dictWrapperModels);
jsonObj.put(MsgArgsType.DICT_MODEL_TYPE.toString(), DictModelType.COLLECTION);
jsonObj.put(MsgArgsType.DICT_TYPE.toString(), cacheHandleType.toString());
// DICT 字典
baseSubMessage.build(CHANNEL,PushSubType.DICT.toString(),jsonObj);
return baseSubMessage;
}
}

@ -1,61 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.msgs;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import org.opsli.api.wrapper.system.menu.MenuModel;
import org.opsli.api.wrapper.system.options.OptionsModel;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
/**
*
*
* @author Parker
* @date 2020-09-15
*/
@Data
@Accessors(chain = true)
public final class OptionMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL;
private OptionMsgFactory(){}
/**
* -
* @param optionsModel
* @return
*/
public static BaseSubMessage createOptionMsg(OptionsModel optionsModel){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.OPTION_CODE.toString(), optionsModel.getOptionCode());
jsonObj.put(MsgArgsType.OPTION_MODEL_DATA.toString(), optionsModel);
// 参数
baseSubMessage.build(CHANNEL,PushSubType.OPTION.toString(),jsonObj);
return baseSubMessage;
}
}

@ -1,60 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.msgs;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import org.opsli.api.wrapper.system.user.UserOrgRefWebModel;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
/**
*
*
* @author Parker
* @date 2020-09-15
*/
@Data
@Accessors(chain = true)
public final class OrgMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL;
private OrgMsgFactory(){}
/**
* -
* @param orgRefModel
* @return
*/
public static BaseSubMessage createOrgMsg(UserOrgRefWebModel orgRefModel){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.ORG_USER_ID.toString(), orgRefModel.getUserId());
jsonObj.put(MsgArgsType.ORG_USER_DATA.toString(), orgRefModel);
// 组织
baseSubMessage.build(CHANNEL,PushSubType.ORG.toString(),jsonObj);
return baseSubMessage;
}
}

@ -1,60 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.msgs;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import org.opsli.api.wrapper.system.tenant.TenantModel;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
/**
*
*
* @author Parker
* @date 2020-09-15
*/
@Data
@Accessors(chain = true)
public final class TenantMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL;
private TenantMsgFactory(){}
/**
* -
* @param tenantModel
* @return
*/
public static BaseSubMessage createTenantMsg(TenantModel tenantModel){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.TENANT_ID.toString(), tenantModel.getId());
jsonObj.put(MsgArgsType.TENANT_DATA.toString(), tenantModel);
// 租户
baseSubMessage.build(CHANNEL,PushSubType.TENANT.toString(),jsonObj);
return baseSubMessage;
}
}

@ -1,122 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.msgs;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import org.opsli.api.wrapper.system.menu.MenuModel;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.core.cache.pushsub.enums.MsgArgsType;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.enums.UserModelType;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
import java.util.List;
/**
*
*
* @author Parker
* @date 2020-09-15
*/
@Data
@Accessors(chain = true)
public final class UserMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL;
private UserMsgFactory(){}
/**
* -
* @param userModel
* @return
*/
public static BaseSubMessage createUserMsg(UserModel userModel){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.USER_ID.toString(), userModel.getId());
jsonObj.put(MsgArgsType.USER_USERNAME.toString(), userModel.getUsername());
jsonObj.put(MsgArgsType.USER_MODEL_TYPE.toString(), UserModelType.USER_MODEL.toString());
jsonObj.put(MsgArgsType.USER_MODEL_DATA.toString(), userModel);
// 用户
baseSubMessage.build(CHANNEL,PushSubType.USER.toString(),jsonObj);
return baseSubMessage;
}
/**
* -
* @param userId ID
* @param roles
* @return
*/
public static BaseSubMessage createUserRolesMsg(String userId, List<String> roles){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.USER_ID.toString(), userId);
jsonObj.put(MsgArgsType.USER_MODEL_TYPE.toString(), UserModelType.USER_ROLES_MODEL.toString());
jsonObj.put(MsgArgsType.USER_MODEL_DATA.toString(), roles);
// 用户
baseSubMessage.build(CHANNEL,PushSubType.USER.toString(),jsonObj);
return baseSubMessage;
}
/**
* -
* @param userId ID
* @param perms
* @return
*/
public static BaseSubMessage createUserPermsMsg(String userId, List<String> perms){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.USER_ID.toString(), userId);
jsonObj.put(MsgArgsType.USER_MODEL_TYPE.toString(), UserModelType.USER_PERMS_MODEL.toString());
jsonObj.put(MsgArgsType.USER_MODEL_DATA.toString(), perms);
// 用户
baseSubMessage.build(CHANNEL,PushSubType.USER.toString(),jsonObj);
return baseSubMessage;
}
/**
* -
* @param userId ID
* @param menus
* @return
*/
public static BaseSubMessage createUserMenusMsg(String userId, List<MenuModel> menus){
BaseSubMessage baseSubMessage = new BaseSubMessage();
// 数据
JSONObject jsonObj = new JSONObject();
jsonObj.put(MsgArgsType.USER_ID.toString(), userId);
jsonObj.put(MsgArgsType.USER_MODEL_TYPE.toString(), UserModelType.USER_MENU_MODEL.toString());
jsonObj.put(MsgArgsType.USER_MODEL_DATA.toString(), menus);
// 用户
baseSubMessage.build(CHANNEL,PushSubType.USER.toString(),jsonObj);
return baseSubMessage;
}
}

@ -1,132 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache.pushsub.receiver;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.opsli.common.enums.SystemInfo;
import org.opsli.core.cache.pushsub.enums.PushSubType;
import org.opsli.core.cache.pushsub.handler.RedisPushSubHandler;
import org.opsli.core.msg.CoreMsg;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
import org.opsli.plugins.redis.pushsub.receiver.BaseReceiver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Redis
*
*
* @author Parker
* @date 2020-09-15
*/
@Slf4j
@Configuration
public class RedisPushSubReceiver extends BaseReceiver {
/** Spring Bean 前缀 */
public static final String SPRING_PREFIX = "redisPushSub_";
/** 监听信道 */
public static final String CHANNEL = "opsli";
/** 处理方法集合 */
private static final ConcurrentMap<PushSubType, RedisPushSubHandler> HANDLER_MAP = new ConcurrentHashMap<>();
@Autowired
private AutowireCapableBeanFactory beanFactory;
@Autowired
private DefaultListableBeanFactory defaultListableBeanFactory;
public RedisPushSubReceiver() {
super(CHANNEL);
}
@Bean
public void initRedisPushSubHandler(){
// 拿到state包下 实现了 SystemEventState 接口的,所有子类
Set<Class<?>> clazzSet = ClassUtil.scanPackageBySuper(
RedisPushSubHandler.class.getPackage().getName(),
RedisPushSubHandler.class
);
for (Class<?> aClass : clazzSet) {
// 位运算 去除抽象类
if((aClass.getModifiers() & Modifier.ABSTRACT) != 0){
continue;
}
try {
Object obj = aClass.newInstance();
RedisPushSubHandler handler = (RedisPushSubHandler) obj;
// 加入集合
HANDLER_MAP.put(handler.getType(),handler);
//将new出的对象放入Spring容器中
defaultListableBeanFactory.registerSingleton(SPRING_PREFIX+aClass.getSimpleName(), obj);
//自动注入依赖
beanFactory.autowireBean(obj);
} catch (Exception e){
log.error(CoreMsg.REDIS_EXCEPTION_PUSH_SUB.getMessage());
}
}
}
@Override
public void receiveMessage(String msg) {
if(msg == null || "".equals(msg)){
return;
}
long beginTime = System.currentTimeMillis();
// 替换 转意符
String replaceAll = msg.replaceAll("\\\\", "");
String substring = replaceAll.substring(1, replaceAll.length() - 1);
JSONObject msgJson = JSONObject.parseObject(substring);
String type = (String) msgJson.get(BaseSubMessage.BASE_TYPE);
String identifier = (String) msgJson.get(BaseSubMessage.BASE_ID);
// 本机不广播
if(SystemInfo.INSTANCE.getSystemID().equals(identifier)){
return;
}
PushSubType pt = PushSubType.valueOf(type);
RedisPushSubHandler redisPushSubHandler = HANDLER_MAP.get(pt);
if(redisPushSubHandler == null){
return;
}
redisPushSubHandler.handler(msgJson);
long endTime = System.currentTimeMillis();
log.info("订阅节点更新缓存 耗时(毫秒):{}",(endTime-beginTime));
}
}

@ -1,299 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.filters.aspect;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.opsli.api.base.warpper.ApiWrapper;
import org.opsli.common.annotation.hotdata.EnableHotData;
import org.opsli.common.annotation.hotdata.HotDataDel;
import org.opsli.common.annotation.hotdata.HotDataPut;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.entity.CacheDataEntity;
import org.opsli.core.cache.pushsub.enums.CacheHandleType;
import org.opsli.core.cache.pushsub.msgs.CacheDataMsgFactory;
import org.opsli.plugins.redis.RedisPlugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import static org.opsli.common.constants.OrderConstants.HOT_DATA_ORDER;
/**
*
*
* @author parker
* @date 2020-09-16
*/
@Slf4j
@Order(HOT_DATA_ORDER)
@Aspect
@Component
public class CacheDataAop {
@Autowired
private RedisPlugin redisPlugin;
@Pointcut("@annotation(org.opsli.common.annotation.hotdata.HotDataPut)")
public void hotDataPut() {
}
@Pointcut("@annotation(org.opsli.common.annotation.hotdata.HotDataDel)")
public void hotDataDel() {
}
/**
*
* @param point point
* @return Object
* @throws Throwable
*/
@Around("hotDataPut()")
public Object hotDataPutProcess(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
Object returnValue = point.proceed(args);
// 判断 方法上是否使用 EnableHotData注解 如果没有表示开启热数据 则直接跳过
Annotation annotation = point.getTarget().getClass().getAnnotation(EnableHotData.class);
if(annotation == null){
return returnValue;
}
List<CacheDataEntity> cacheDataEntityList = this.putHandlerData(point, returnValue);
// 非法判断
if(CollUtil.isEmpty(cacheDataEntityList)){
return returnValue;
}
for (CacheDataEntity cacheDataEntity : cacheDataEntityList) {
// 更新缓存数据
// 热点数据
boolean putRet = CacheUtil.put(CacheConstants.HOT_DATA_PREFIX +":"+ cacheDataEntity.getKey(),
returnValue);
if(putRet){
// 广播缓存数据 - 通知其他服务器同步数据
redisPlugin.sendMessage(
CacheDataMsgFactory.createMsg(
cacheDataEntity, returnValue, CacheHandleType.UPDATE)
);
}
}
return returnValue;
}
/**
*
* @param point point
* @return Object
* @throws Throwable
*/
@Around("hotDataDel()")
public Object hotDataDelProcess(ProceedingJoinPoint point) throws Throwable {
Object[] args= point.getArgs();
Object returnValue = point.proceed(args);
// 判断 方法上是否使用 EnableHotData注解 如果没有表示开启热数据 则直接跳过
Annotation annotation = point.getTarget().getClass().getAnnotation(EnableHotData.class);
if(annotation == null){
return returnValue;
}
// 删除状态判断
try {
Boolean ret = (Boolean) returnValue;
if(ret == null || !ret){
return returnValue;
}
}catch (Exception e){
log.error(e.getMessage(),e);
return returnValue;
}
List<CacheDataEntity> cacheDataEntityList = this.delHandlerData(point, args);
// 非法判断
if(CollUtil.isEmpty(cacheDataEntityList)){
return returnValue;
}
for (CacheDataEntity cacheDataEntity : cacheDataEntityList) {
// 更新缓存数据 - 删除缓存
boolean delRet = CacheUtil.del(CacheConstants.HOT_DATA_PREFIX +":"+ cacheDataEntity.getKey());
if(delRet){
// 广播缓存数据 - 通知其他服务器同步数据
redisPlugin.sendMessage(
CacheDataMsgFactory.createMsg(
cacheDataEntity, null, CacheHandleType.DELETE)
);
}
}
return returnValue;
}
// ===========================================================================
/***
* PUT
* @param point point
*/
private List<CacheDataEntity> putHandlerData(ProceedingJoinPoint point, Object returnValue){
// 这里 只对 继承了 ApiWrapper 的类做处理
if(!(returnValue instanceof ApiWrapper)){
return null;
}
// 消息集合 后续可能会考虑 多消息存储
List<CacheDataEntity> cacheDataEntities = Lists.newArrayListWithCapacity(1);
// 报错不处理
try {
// 获得方法
Method objMethod = this.getMethod(point);
if(objMethod == null) {
return null;
}
// 获取注解参数
HotDataPut aCache = objMethod.getAnnotation(HotDataPut.class);
if(aCache != null){
// 这里 只对 继承了 BaseEntity 的类做处理
ApiWrapper apiWrapper = (ApiWrapper) returnValue;
CacheDataEntity ret = new CacheDataEntity(apiWrapper.getId());
// 存放数据
this.putCacheData(cacheDataEntities, ret);
return cacheDataEntities;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
/***
* DEL
* @param point point
*/
private List<CacheDataEntity> delHandlerData(ProceedingJoinPoint point, Object[] args){
if(args == null || args.length == 0){
return null;
}
// 消息集合
List<CacheDataEntity> cacheDataEntities = Lists.newArrayListWithCapacity(args.length);
// 报错不处理
try {
// 获得方法
Method objMethod = this.getMethod(point);
if(objMethod == null) {
return null;
}
// 获取注解参数
HotDataDel aCache= objMethod.getAnnotation(HotDataDel.class);
if(aCache != null){
List<String> keyList = null;
// 处理数据
for (Object arg : args) {
if (arg instanceof ApiWrapper) {
// key 存储ID
ApiWrapper apiWrapper = Convert.convert(ApiWrapper.class, arg);
keyList = Convert.toList(String.class, apiWrapper.getId());
} else if (arg instanceof Collection) {
try {
keyList = Lists.newArrayList();
List<ApiWrapper> baseEntityList = Convert.toList(ApiWrapper.class, arg);
for (ApiWrapper baseEntity : baseEntityList) {
keyList.add(baseEntity.getId());
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
}else {
keyList = Convert.toList(String.class, arg);
}
}
if(keyList != null && CollUtil.isNotEmpty(keyList)){
for (String key : keyList) {
CacheDataEntity ret = new CacheDataEntity(key);
// 存放数据
this.putCacheData(cacheDataEntities, ret);
}
}
return cacheDataEntities;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
// =====================
/**
*
* @param point point
* @return Method
*/
private Method getMethod(ProceedingJoinPoint point){
Method m = null;
try {
String methodName= point.getSignature().getName();
Class<?> classTarget= point.getTarget().getClass();
Class<?>[] par=((MethodSignature) point.getSignature()).getParameterTypes();
m = classTarget.getMethod(methodName, par);
}catch (Exception ignored){}
return m;
}
/**
*
* @param cacheDataList
* @param cacheData
*/
private void putCacheData(List<CacheDataEntity> cacheDataList, CacheDataEntity cacheData){
// 非法判断
if(cacheDataList == null){
return;
}
cacheDataList.add(cacheData);
}
}

@ -21,8 +21,11 @@ import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.TypeUtil;
import lombok.extern.slf4j.Slf4j;
import opsli.plugins.crypto.CryptoPlugin;
import opsli.plugins.crypto.enums.CryptoSymmetricType;
import opsli.plugins.crypto.model.CryptoAsymmetric;
import opsli.plugins.crypto.model.CryptoSymmetric;
import opsli.plugins.crypto.strategy.CryptoAsymmetricService;
import opsli.plugins.crypto.strategy.CryptoSymmetricService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@ -30,7 +33,7 @@ import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.opsli.api.base.encrypt.BaseEncrypt;
import org.opsli.api.base.result.ResultVo;
import org.opsli.common.annotation.ApiCryptoAsymmetric;
import org.opsli.common.annotation.LoginCrypto;
import org.opsli.common.exception.ServiceException;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.options.CryptoConfigFactory;
@ -45,7 +48,7 @@ import java.util.Map;
import static org.opsli.common.constants.OrderConstants.ENCRYPT_ADN_DECRYPT_AOP_SORT;
/**
* Api
*
*
* @author parker
* @date 2021-01-23
@ -54,9 +57,9 @@ import static org.opsli.common.constants.OrderConstants.ENCRYPT_ADN_DECRYPT_AOP_
@Order(ENCRYPT_ADN_DECRYPT_AOP_SORT)
@Aspect
@Component
public class ApiCryptoAsymmetricAop {
public class LoginCryptoAop {
@Pointcut("@annotation(org.opsli.common.annotation.ApiCryptoAsymmetric)")
@Pointcut("@annotation(org.opsli.common.annotation.LoginCrypto)")
public void encryptAndDecrypt() {
}
@ -75,12 +78,12 @@ public class ApiCryptoAsymmetricAop {
// 获得 方法
Method method = signature.getMethod();
// 获得方法注解
ApiCryptoAsymmetric annotation =
method.getAnnotation(ApiCryptoAsymmetric.class);
LoginCrypto annotation =
method.getAnnotation(LoginCrypto.class);
// 获得非对称加解密 执行器
CryptoAsymmetricService asymmetric = null;
// 加解密模型
// 非对称加解密模型
CryptoAsymmetric cryptoAsymmetric = null;
if(annotation != null && annotation.enable()){
asymmetric = CryptoPlugin.getAsymmetric();
@ -88,7 +91,7 @@ public class ApiCryptoAsymmetricAop {
}
// 1. 请求解密
if(annotation != null && annotation.enable() && annotation.requestDecrypt()){
if(annotation != null && annotation.enable()){
if(cryptoAsymmetric != null){
enterDecrypt(args, method, asymmetric, cryptoAsymmetric);
}
@ -97,10 +100,16 @@ public class ApiCryptoAsymmetricAop {
// 2. 执行方法
returnValue = point.proceed(args);
// 3. 返回加密
if(annotation != null && annotation.enable() && annotation.responseEncrypt()){
// 3. 返回加密 返回加密为对称加密
if(annotation != null && annotation.enable()){
if(cryptoAsymmetric != null){
returnValue = resultEncrypt(returnValue, asymmetric, cryptoAsymmetric);
CryptoSymmetricService symmetric = CryptoPlugin.getSymmetric();
CryptoSymmetric symmetricModel = symmetric.createNilModel();
symmetricModel.setCryptoType(CryptoSymmetricType.DES);
symmetricModel.setPrivateKey(cryptoAsymmetric.getPublicKey());
// 执行加密操作
returnValue = resultEncrypt(returnValue, symmetric, symmetricModel);
}
}
return returnValue;
@ -152,12 +161,12 @@ public class ApiCryptoAsymmetricAop {
/**
*
* @param returnValue
* @param asymmetric
* @param cryptoModel
* @param symmetric
* @param cryptoModel
* @return Object
*/
@SuppressWarnings("unchecked")
private Object resultEncrypt(Object returnValue, CryptoAsymmetricService asymmetric, CryptoAsymmetric cryptoModel) {
private Object resultEncrypt(Object returnValue, CryptoSymmetricService symmetric, CryptoSymmetric cryptoModel) {
if(returnValue != null){
try {
// 执行加密过程
@ -165,10 +174,10 @@ public class ApiCryptoAsymmetricAop {
// 重新赋值 data
ResultVo<Object> ret = (ResultVo<Object>) returnValue;
ret.setData(
asymmetric.encrypt(cryptoModel, ret.getData())
symmetric.encrypt(cryptoModel, ret.getData())
);
}else {
returnValue = asymmetric.encrypt(cryptoModel, returnValue);
returnValue = symmetric.encrypt(cryptoModel, returnValue);
}
}catch (Exception e){
// 非对称加密失败

@ -80,8 +80,8 @@ public class Page<T extends BaseEntity,E extends ApiWrapper> extends PageSeriali
/**
* count
*/
public void pageHelperBegin(boolean countFlag){
PageHelper.startPage(this.pageNo,this.pageSize, countFlag);
public void pageHelperBegin(boolean isByCount){
PageHelper.startPage(this.pageNo,this.pageSize, isByCount);
}
/**

@ -23,7 +23,7 @@ import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.exception.TokenException;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.msg.TokenMsg;
import org.opsli.plugins.redis.RedisPlugin;
@ -98,8 +98,11 @@ public class CaptchaUtil {
// 生成验证码
Captcha captcha = captchaStrategy.createCaptcha();
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX + uuid);
// 保存至缓存
boolean ret = redisPlugin.put(CacheUtil.getPrefixName() + PREFIX + uuid, captcha.text(), TIME_OUT);
boolean ret = redisPlugin.put(cacheKey, captcha.text(), TIME_OUT);
if(ret){
// 输出
captcha.out(out);
@ -127,8 +130,11 @@ public class CaptchaUtil {
throw new TokenException(TokenMsg.EXCEPTION_CAPTCHA_CODE_NULL);
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX + uuid);
// 验证码
String codeTemp = (String) redisPlugin.get(CacheUtil.getPrefixName() + PREFIX + uuid);
String codeTemp = (String) redisPlugin.get(cacheKey);
if (StringUtils.isEmpty(codeTemp)) {
throw new TokenException(TokenMsg.EXCEPTION_CAPTCHA_NULL);
}
@ -156,8 +162,11 @@ public class CaptchaUtil {
return false;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX + uuid);
//删除验证码
return redisPlugin.del(CacheUtil.getPrefixName() + PREFIX + uuid);
return redisPlugin.del(cacheKey);
}
// ======================

@ -19,19 +19,20 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.web.system.dict.DictDetailApi;
import org.opsli.api.wrapper.system.dict.DictDetailModel;
import org.opsli.api.wrapper.system.dict.DictWrapper;
import org.opsli.common.constants.DictConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.common.constants.RedisConstants;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.opsli.plugins.redis.RedisPlugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@ -56,6 +57,8 @@ public class DictUtil {
/** 字典Service */
private static DictDetailApi dictDetailApi;
/** Redis */
private static RedisTemplate<String, Object> redisTemplate;
/**
*
@ -70,75 +73,30 @@ public class DictUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = DictConstants.CACHE_PREFIX_VALUE + typeCode;
// 缓存Key + VALUE
String cacheKeyVal = cacheKey + ":" + dictValue;
// 字典名称
String dictName = null;
DictDetailModel cacheModel = CacheUtil.getHash(DictDetailModel.class, cacheKey,
dictValue);
// 如果缓存有值 直接返回
if (cacheModel != null &&
StringUtils.isNotEmpty(cacheModel.getDictName())){
return cacheModel.getDictName();
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_DICT_VALUE + typeCode);
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKeyVal);
if(hasNilFlag){
return defaultVal;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKeyVal)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
Object cache = SecurityCache.hGet(redisTemplate, cacheKey, dictValue, (k) -> {
// 查询数据库 并保存到缓存内
ResultVo<List<DictDetailModel>> resultVo = dictDetailApi.findListByTypeCode(typeCode);
if (!resultVo.isSuccess()) {
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
cacheModel = CacheUtil.getHash(DictDetailModel.class, cacheKey,
dictValue);
// 如果缓存有值 直接返回
if (cacheModel != null &&
StringUtils.isNotEmpty(cacheModel.getDictName())){
return cacheModel.getDictName();
}
// 查询数据库 并保存到缓存内
ResultVo<List<DictDetailModel>> resultVo = dictDetailApi.findListByTypeCode(typeCode);
if(resultVo.isSuccess()){
List<DictDetailModel> dictDetailModels = resultVo.getData();
for (DictDetailModel model : dictDetailModels) {
if(model.getDictValue().equals(dictValue)){
// 保存至缓存
DictWrapper dictWrapper = DictUtil.putByModel(model);
// 缓存名
dictName = dictWrapper.getDictName();
break;
}
List<DictDetailModel> dictDetailModels = resultVo.getData();
for (DictDetailModel model : dictDetailModels) {
if (model.getDictValue().equals(dictValue)) {
// 转化Model
return DictUtil.formatModel(model);
}
}
return null;
});
}catch (Exception e){
log.error(e.getMessage(),e);
if(null == cache){
return defaultVal;
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKeyVal);
}
// 如果名称还是 为空 则赋默认值
if(StringUtils.isEmpty(dictName)){
// 加入缓存防穿透
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKeyVal);
dictName = defaultVal;
}
return dictName;
DictWrapper dictWrapper = Convert.convert(DictWrapper.class, cache);
return dictWrapper.getDictName();
}
/**
@ -153,75 +111,32 @@ public class DictUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = DictConstants.CACHE_PREFIX_NAME + typeCode;
// 缓存Key + VALUE
String cacheKeyVal = cacheKey + ":" + dictName;
// 字典值
String dictValue = null;
DictDetailModel cacheModel = CacheUtil.getHash(DictDetailModel.class, cacheKey,
dictName);
// 如果缓存有值 直接返回
if (cacheModel != null &&
StringUtils.isNotEmpty(cacheModel.getDictValue())){
return cacheModel.getDictValue();
}
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKeyVal);
if(hasNilFlag){
return defaultVal;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_DICT_NAME + typeCode);
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKeyVal)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
Object cache = SecurityCache.hGet(redisTemplate, cacheKey, dictName, (k) -> {
// 查询数据库 并保存到缓存内
ResultVo<List<DictDetailModel>> resultVo = dictDetailApi.findListByTypeCode(typeCode);
if (!resultVo.isSuccess()) {
return null;
}
cacheModel = CacheUtil.getHash(DictDetailModel.class, cacheKey,
dictName);
// 如果缓存有值 直接返回
if (cacheModel != null &&
StringUtils.isNotEmpty(cacheModel.getDictValue())){
return cacheModel.getDictValue();
}
// 查询数据库 并保存到缓存内
ResultVo<List<DictDetailModel>> resultVo = dictDetailApi.findListByTypeCode(typeCode);
if(resultVo.isSuccess()){
List<DictDetailModel> dictDetailModels = resultVo.getData();
for (DictDetailModel model : dictDetailModels) {
if(model.getDictName().equals(dictName)){
// 保存至缓存
DictWrapper dictWrapper = DictUtil.putByModel(model);
// 值
dictValue = dictWrapper.getDictValue();
break;
}
List<DictDetailModel> dictDetailModels = resultVo.getData();
for (DictDetailModel model : dictDetailModels) {
if (model.getDictName().equals(dictName)) {
// 转化Model
return DictUtil.formatModel(model);
}
}
return null;
});
}catch (Exception e){
log.error(e.getMessage(),e);
if(null == cache){
return defaultVal;
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKeyVal);
}
// 如果值还是 为空 则赋默认值
if(StringUtils.isEmpty(dictValue)){
// 加入缓存防穿透
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKeyVal);
dictValue = defaultVal;
}
return dictValue;
DictWrapper dictWrapper = Convert.convert(DictWrapper.class, cache);
return dictWrapper.getDictValue();
}
/**
@ -235,134 +150,36 @@ public class DictUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = DictConstants.CACHE_PREFIX_NAME + typeCode;
// 处理集合数据
List<DictWrapper> dictWrapperModels = handleDictList(
CacheUtil.getHashAll(cacheKey), typeCode);
if(CollUtil.isNotEmpty(dictWrapperModels)){
return sortDictWrappers(dictWrapperModels);
}
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return sortDictWrappers(dictWrapperModels);
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return sortDictWrappers(dictWrapperModels);
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
// 处理集合数据
dictWrapperModels = handleDictList(
CacheUtil.getHashAll(cacheKey), typeCode);
if(CollUtil.isNotEmpty(dictWrapperModels)){
return sortDictWrappers(dictWrapperModels);
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_DICT_VALUE + typeCode);
Map<String, Object> dictCacheMap = SecurityCache.hGetAll(redisTemplate, cacheKey, (k) -> {
// 查询数据库 并保存到缓存内
ResultVo<List<DictDetailModel>> resultVo = dictDetailApi.findListByTypeCode(typeCode);
if(resultVo.isSuccess()){
List<DictDetailModel> dictDetailModels = resultVo.getData();
// 处理数据库查询数据
if(CollUtil.isNotEmpty(dictDetailModels)){
dictWrapperModels = Lists.newArrayListWithCapacity(dictDetailModels.size());
for (DictDetailModel model : dictDetailModels) {
// 保存至缓存
DictWrapper dictWrapper = DictUtil.putByModel(model);
dictWrapperModels.add(dictWrapper);
}
return sortDictWrappers(dictWrapperModels);
}
if (!resultVo.isSuccess()) {
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
List<DictDetailModel> dictDetailModels = resultVo.getData();
Map<String, Object> dictMap = Maps.newHashMapWithExpectedSize(dictDetailModels.size());
for (DictDetailModel model : dictDetailModels) {
dictMap.put(model.getDictValue(), model);
}
return dictMap;
});
// 如果值还是 为空 则赋默认值
if(CollUtil.isEmpty(dictWrapperModels)){
// 加入缓存防穿透
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
}
List<DictWrapper> dictWrappers = handleDictList(dictCacheMap, typeCode);
// 排序
return sortDictWrappers(dictWrapperModels);
return sortDictWrappers(dictWrappers);
}
/**
*
* @param dictWrapperModels Model
* @return List
*/
private static List<DictWrapper> sortDictWrappers(List<DictWrapper> dictWrapperModels) {
// 非法判读
if(dictWrapperModels == null){
return null;
}
ListUtil.sort(dictWrapperModels, (o1, o2) -> {
int oInt1 = Integer.MAX_VALUE;
int oInt2 = Integer.MAX_VALUE;
if(o1 != null && o1.getModel() != null){
oInt1 = o1.getModel().getSortNo()==null?oInt1:o1.getModel().getSortNo();
}
if(o2 != null && o2.getModel() != null){
oInt2 = o2.getModel().getSortNo()==null?oInt2:o2.getModel().getSortNo();
}
return Integer.compare(oInt1, oInt2);
});
return dictWrapperModels;
}
// ===============
/**
*
* @param model
*/
private static DictWrapper putByModel(DictDetailModel model){
DictWrapper dictWrapperModel = new DictWrapper();
dictWrapperModel.setTypeCode(model.getTypeCode());
dictWrapperModel.setDictName(model.getDictName());
dictWrapperModel.setDictValue(model.getDictValue());
dictWrapperModel.setModel(model);
// 保存至缓存
DictUtil.put(dictWrapperModel);
return dictWrapperModel;
}
/**
*
* @param model
*/
public static void put(DictWrapper model){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 清除缓存
DictUtil.del(model);
CacheUtil.putHash(DictConstants.CACHE_PREFIX_NAME + model.getTypeCode(),
model.getDictName(), model.getModel());
CacheUtil.putHash(DictConstants.CACHE_PREFIX_VALUE + model.getTypeCode(),
model.getDictValue(), model.getModel());
}
/**
*
@ -378,60 +195,29 @@ public class DictUtil {
return true;
}
boolean hasNilFlagByName = CacheUtil.hasNilFlag(DictConstants.CACHE_PREFIX_NAME +
model.getTypeCode() + ":" + model.getDictName());
boolean hasNilFlagByValue = CacheUtil.hasNilFlag(DictConstants.CACHE_PREFIX_VALUE +
model.getTypeCode() + ":" + model.getDictValue());
// 缓存Key
String cacheKeyByValue = CacheUtil.formatKey(
RedisConstants.PREFIX_DICT_VALUE + model.getTypeCode());
// 缓存Key
String cacheKeyByName = CacheUtil.formatKey(
RedisConstants.PREFIX_DICT_NAME + model.getTypeCode());
DictWrapper dictByName = CacheUtil.getHash(DictWrapper.class,
DictConstants.CACHE_PREFIX_NAME + model.getTypeCode(),
model.getDictName());
DictWrapper dictByValue = CacheUtil.getHash(DictWrapper.class,
DictConstants.CACHE_PREFIX_VALUE + model.getTypeCode(),
model.getDictValue());
// 计数器
int count = 0;
if (hasNilFlagByName){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(DictConstants.CACHE_PREFIX_NAME +
model.getTypeCode() + ":" + model.getDictName());
int count = 2;
{
boolean tmp = SecurityCache.hDel(redisTemplate, cacheKeyByValue, model.getDictValue());
if(tmp){
count--;
}
}
if (hasNilFlagByValue){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(DictConstants.CACHE_PREFIX_VALUE +
model.getTypeCode() + ":" + model.getDictValue());
{
boolean tmp = SecurityCache.hDel(redisTemplate, cacheKeyByName, model.getDictName());
if(tmp){
count--;
}
}
if (dictByName != null){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delHash(DictConstants.CACHE_PREFIX_NAME +
model.getTypeCode(), model.getDictName());
if(tmp){
count--;
}
}
if (dictByValue != null){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delHash(DictConstants.CACHE_PREFIX_VALUE +
model.getTypeCode(), model.getDictValue());
if(tmp){
count--;
}
}
return count == 0;
}
@ -450,15 +236,53 @@ public class DictUtil {
return true;
}
// 计数器
int count = dictWrapperList.size();
for (DictWrapper dictWrapperModel : dictWrapperList) {
boolean tmp = DictUtil.del(dictWrapperModel);
if(tmp){
count--;
}
// 缓存Key
String cacheKeyByValue = CacheUtil.formatKey(
RedisConstants.PREFIX_DICT_VALUE + typeCode);
// 缓存Key
String cacheKeyByName = CacheUtil.formatKey(
RedisConstants.PREFIX_DICT_NAME + typeCode);
return SecurityCache.removeMore(redisTemplate, cacheKeyByValue, cacheKeyByName);
}
/**
*
* @param model
*/
private static DictWrapper formatModel(DictDetailModel model){
DictWrapper dictWrapperModel = new DictWrapper();
dictWrapperModel.setTypeCode(model.getTypeCode());
dictWrapperModel.setDictName(model.getDictName());
dictWrapperModel.setDictValue(model.getDictValue());
dictWrapperModel.setModel(model);
return dictWrapperModel;
}
/**
*
* @param dictWrapperModels Model
* @return List
*/
private static List<DictWrapper> sortDictWrappers(List<DictWrapper> dictWrapperModels) {
// 非法判读
if(dictWrapperModels == null){
return null;
}
return count == 0;
ListUtil.sort(dictWrapperModels, (o1, o2) -> {
int oInt1 = Integer.MAX_VALUE;
int oInt2 = Integer.MAX_VALUE;
if(o1 != null && o1.getModel() != null){
oInt1 = o1.getModel().getSortNo()==null?oInt1:o1.getModel().getSortNo();
}
if(o2 != null && o2.getModel() != null){
oInt2 = o2.getModel().getSortNo()==null?oInt2:o2.getModel().getSortNo();
}
return Integer.compare(oInt1, oInt2);
});
return dictWrapperModels;
}
/***
@ -496,9 +320,10 @@ public class DictUtil {
*
*/
@Autowired
public void init(DictDetailApi dictDetailApi) {
public void init(DictDetailApi dictDetailApi,
RedisTemplate<String, Object> redisTemplate) {
DictUtil.dictDetailApi = dictDetailApi;
DictUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -16,7 +16,7 @@
package org.opsli.core.utils;
import lombok.extern.slf4j.Slf4j;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.msg.CoreMsg;
import org.opsli.plugins.redisson.RedissonLock;
import org.springframework.beans.factory.annotation.Autowired;
@ -60,7 +60,9 @@ public class DistributedLockUtil {
boolean isLock = true;
// 分布式上锁
if(REDISSON_LOCK != null){
isLock = REDISSON_LOCK.tryLock(CacheUtil.getPrefixName() + lockName, LEASE_TIME);
// 缓存Key
String cacheKey = CacheUtil.formatKey(lockName);
isLock = REDISSON_LOCK.tryLock(cacheKey, LEASE_TIME);
}
return isLock;
}
@ -76,7 +78,9 @@ public class DistributedLockUtil {
// 释放锁
if(REDISSON_LOCK != null){
REDISSON_LOCK.unlockByThread(CacheUtil.getPrefixName() + lockName);
// 缓存Key
String cacheKey = CacheUtil.formatKey(lockName);
REDISSON_LOCK.unlockByThread(cacheKey);
}
}

@ -15,16 +15,20 @@
*/
package org.opsli.core.utils;
import cn.hutool.core.convert.Convert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.web.system.menu.MenuApi;
import org.opsli.api.wrapper.system.menu.MenuModel;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.common.constants.RedisConstants;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@ -41,15 +45,14 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@Lazy(false)
public class MenuUtil {
/** 前缀 */
public static final String PREFIX_CODE = "menu:code:";
/** 菜单 Api */
private static MenuApi menuApi;
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
private static RedisTemplate<String, Object> redisTemplate;
/**
*
* @param permissions
@ -60,57 +63,20 @@ public class MenuUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_CODE + permissions;
// 先从缓存里拿
MenuModel menuModel = CacheUtil.getTimed(MenuModel.class, cacheKey);
if (menuModel != null){
return menuModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
menuModel = CacheUtil.getTimed(MenuModel.class, cacheKey);
if (menuModel != null){
return menuModel;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_MENU_CODE + permissions);
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<MenuModel> resultVo = menuApi.getByPermissions(permissions);
if(resultVo.isSuccess()){
menuModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, menuModel);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(menuModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return menuModel;
return Convert.convert(MenuModel.class, cache);
}
@ -130,46 +96,24 @@ public class MenuUtil {
return true;
}
// 计数器
int count = 0;
MenuModel model = CacheUtil.getTimed(MenuModel.class, PREFIX_CODE + menu.getPermissions());
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_CODE + menu.getPermissions());
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_CODE + menu.getPermissions());
if(tmp){
count--;
}
}
if(model != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_CODE + menu.getPermissions());
if(tmp){
count--;
}
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_MENU_CODE + menu.getPermissions());
return count == 0;
// 删除缓存
return SecurityCache.remove(redisTemplate, cacheKey);
}
// =====================================
/**
*
*/
@Autowired
public void init(MenuApi menuApi) {
public void init(MenuApi menuApi,
RedisTemplate<String, Object> redisTemplate) {
MenuUtil.menuApi = menuApi;
MenuUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -27,12 +27,15 @@ import org.opsli.api.base.result.ResultVo;
import org.opsli.api.web.system.options.OptionsApi;
import org.opsli.api.wrapper.system.options.OptionsModel;
import org.opsli.common.annotation.OptionDict;
import org.opsli.common.constants.RedisConstants;
import org.opsli.common.enums.OptionsType;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
@ -54,9 +57,6 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@Lazy(false)
public class OptionsUtil {
/** 前缀 */
public static final String PREFIX_CODE = "options:code";
/** 参数 Api */
private static OptionsApi optionsApi;
@ -66,6 +66,8 @@ public class OptionsUtil {
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
private static RedisTemplate<String, Object> redisTemplate;
/**
* optionsType
* @param optionsType
@ -137,58 +139,18 @@ public class OptionsUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_CODE;
// 缓存Key + VALUE
String cacheKeyVal = cacheKey + ":" + optionCode;
// 先从缓存里拿
OptionsModel model = CacheUtil.getHash(OptionsModel.class, cacheKey, optionCode);
if (model != null){
return model;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKeyVal);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKeyVal)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
model = CacheUtil.getHash(OptionsModel.class, cacheKey, optionCode);
if (model != null){
return model;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_OPTIONS_CODE);
Object cache = SecurityCache.hGet(redisTemplate, cacheKey, optionCode, (k) -> {
// 查询数据库
ResultVo<OptionsModel> resultVo = optionsApi.getByCode(optionCode);
if(resultVo.isSuccess()){
model = resultVo.getData();
// 存入缓存
CacheUtil.putHash(cacheKey, optionCode, model);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKeyVal);
}
if(model == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKeyVal);
return null;
}
return resultVo.getData();
});
return model;
return Convert.convert(OptionsModel.class, cache);
}
// ============== 刷新缓存 ==============
@ -208,36 +170,10 @@ public class OptionsUtil {
}
// 缓存Key
String cacheKey = PREFIX_CODE;
// 缓存Key + VALUE
String cacheKeyVal = cacheKey + ":" + option.getOptionCode();
// 计数器
int count = 0;
OptionsModel model = CacheUtil.getHash(OptionsModel.class, cacheKey, option.getOptionCode());
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKeyVal);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(cacheKeyVal);
if(tmp){
count--;
}
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_OPTIONS_CODE);
if(model != null){
count++;
// 先删除
boolean tmp = CacheUtil.delHash(cacheKey, option.getOptionCode());
if(tmp){
count--;
}
}
return count == 0;
// 删除缓存
return SecurityCache.hDel(redisTemplate, cacheKey, option.getOptionCode());
}
/**
@ -292,9 +228,10 @@ public class OptionsUtil {
*
*/
@Autowired
public void init(OptionsApi optionsApi) {
public void init(OptionsApi optionsApi,
RedisTemplate<String, Object> redisTemplate) {
OptionsUtil.optionsApi = optionsApi;
OptionsUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}
}

@ -19,7 +19,8 @@ import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.common.constants.RedisConstants;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.msg.CoreMsg;
import org.opsli.plugins.redis.RedisPlugin;
import org.springframework.beans.factory.annotation.Autowired;
@ -48,8 +49,6 @@ public class SearchHisUtil {
/** 搜索历史缓存数据KEY */
private static final int DEFAULT_COUNT = 10;
/** 缓存前缀 */
private static final String CACHE_PREFIX = "his:username:";
/** Redis插件 */
private static RedisPlugin redisPlugin;
@ -79,7 +78,7 @@ public class SearchHisUtil {
// 获得当前用户
UserModel user = UserUtil.getUser();
String cacheKey = CacheUtil.getPrefixName() + CACHE_PREFIX + user.getUsername() + ":" + key;
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_HIS_USERNAME + user.getUsername() + ":" + key);
return redisPlugin.zReverseRange(cacheKey, 0, count - 1);
}
@ -109,7 +108,7 @@ public class SearchHisUtil {
}
String cacheKey = CacheUtil.getPrefixName() + CACHE_PREFIX + user.getUsername() + ":" + key;
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_HIS_USERNAME + user.getUsername() + ":" + key);
String val = values[0];
// 记录

@ -15,16 +15,19 @@
*/
package org.opsli.core.utils;
import cn.hutool.core.convert.Convert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.web.system.tenant.TenantApi;
import org.opsli.api.wrapper.system.tenant.TenantModel;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@ -53,6 +56,8 @@ public class TenantUtil {
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
private static RedisTemplate<String, Object> redisTemplate;
/**
* tenantId
* @param tenantId ID
@ -64,56 +69,20 @@ public class TenantUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_CODE + tenantId;
// 先从缓存里拿
TenantModel tenantModel = CacheUtil.getTimed(TenantModel.class, cacheKey);
if (tenantModel != null){
return tenantModel;
}
String cacheKey = CacheUtil.formatKey(PREFIX_CODE + tenantId);
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
tenantModel = CacheUtil.getTimed(TenantModel.class, cacheKey);
if (tenantModel != null){
return tenantModel;
}
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<TenantModel> resultVo = tenantApi.getTenantByUsable(tenantId);
if(resultVo.isSuccess()){
tenantModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, tenantModel);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(tenantModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return resultVo.getData();
}, true);
return tenantModel;
return Convert.convert(TenantModel.class, cache);
}
@ -133,32 +102,10 @@ public class TenantUtil {
return true;
}
// 计数器
int count = 0;
TenantModel tenantModel = CacheUtil.getTimed(TenantModel.class, PREFIX_CODE + tenantId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_CODE + tenantId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_CODE + tenantId);
if(tmp){
count--;
}
}
if(tenantModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_CODE + tenantId);
if(tmp){
count--;
}
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX_CODE + tenantId);
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
@ -168,9 +115,10 @@ public class TenantUtil {
*
*/
@Autowired
public void init(TenantApi tenantApi) {
public void init(TenantApi tenantApi,
RedisTemplate<String, Object> redisTemplate) {
TenantUtil.tenantApi = tenantApi;
TenantUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.constants.RedisConstants;
import org.opsli.common.constants.SignConstants;
import org.opsli.common.constants.TokenConstants;
import org.opsli.common.constants.TokenTypeConstants;
@ -35,7 +36,7 @@ import org.opsli.common.enums.LoginLimitRefuse;
import org.opsli.common.exception.TokenException;
import org.opsli.core.api.TokenThreadLocal;
import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.msg.TokenMsg;
import org.opsli.plugins.redis.RedisPlugin;
@ -64,12 +65,6 @@ public class UserTokenUtil {
/** token 缓存名 */
public static final String TOKEN_NAME = TokenConstants.ACCESS_TOKEN;
/** 缓存前缀 */
private static final String TICKET_PREFIX = "ticket:";
/** 账号失败次数 */
public static final String ACCOUNT_SLIP_COUNT_PREFIX = "account:slip:count:";
/** 账号失败锁定KEY */
public static final String ACCOUNT_SLIP_LOCK_PREFIX = "account:slip:lock:";
/** 限制登录数量 -1 为无限大 */
public static final int ACCOUNT_LIMIT_INFINITE = -1;
/** 登录配置信息 */
@ -100,8 +95,8 @@ public class UserTokenUtil {
// 如果当前登录开启 数量限制
if(LOGIN_PROPERTIES.getLimitCount() > ACCOUNT_LIMIT_INFINITE){
// 当前用户已存在 Token数量
Long ticketLen = redisPlugin.sSize(CacheUtil.getPrefixName() +
TICKET_PREFIX + user.getUsername());
Long ticketLen = redisPlugin.sSize(
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()));
if(ticketLen !=null && ticketLen >= LOGIN_PROPERTIES.getLimitCount()){
// 如果是拒绝后者 则直接抛出异常
if(LoginLimitRefuse.AFTER == LOGIN_PROPERTIES.getLimitRefuse()){
@ -110,7 +105,9 @@ public class UserTokenUtil {
}
// 如果是拒绝前者 则弹出前者
else {
redisPlugin.sPop(CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername());
redisPlugin.sPop(
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername())
);
}
}
}
@ -135,11 +132,13 @@ public class UserTokenUtil {
// 在redis存一份 token 是为了防止 人为造假
// 保存用户token
Long saveLong = redisPlugin.sPut(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername(), signToken);
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()),
signToken
);
if(saveLong != null && saveLong > 0){
// 设置该用户全部token失效时间 如果这时又有新设备登录 则续命
redisPlugin.expire(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername(),
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()),
JwtUtil.EXPIRE_MILLISECOND, TimeUnit.MILLISECONDS);
TokenRet tokenRet = new TokenRet();
@ -249,11 +248,13 @@ public class UserTokenUtil {
if(user != null){
// 删除Token信息
redisPlugin.sRemove(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername(), token);
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()),
token);
// 如果缓存中 无该用户任何Token信息 则删除用户缓存
Long size = redisPlugin.sSize(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername());
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername())
);
if(size == null || size == 0L) {
// 删除相关信息
UserUtil.refreshUser(user);
@ -296,7 +297,8 @@ public class UserTokenUtil {
String username = getUserNameByToken(token);
boolean hashKey = redisPlugin.sHashKey(
CacheUtil.getPrefixName() + TICKET_PREFIX + username, token);
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + username),
token);
if(!hashKey){
return false;
}
@ -305,7 +307,7 @@ public class UserTokenUtil {
if(BooleanUtil.isTrue(LOGIN_PROPERTIES.getReviveMode())){
// 设置该用户全部token失效时间 如果这时又有新设备登录 则续命
redisPlugin.expire(
CacheUtil.getPrefixName() + TICKET_PREFIX + username,
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + username),
JwtUtil.EXPIRE_MILLISECOND, TimeUnit.MILLISECONDS);
}
@ -329,7 +331,7 @@ public class UserTokenUtil {
// 判断账号是否临时锁定
Long loseTimeMillis = (Long) redisPlugin.get(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_LOCK_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_LOCK + username));
if(loseTimeMillis != null){
Date currDate = DateUtil.date();
DateTime loseDate = DateUtil.date(loseTimeMillis);
@ -364,18 +366,19 @@ public class UserTokenUtil {
// 如果失败次数 超过阈值 则锁定账号
Long slipNum = redisPlugin.increment(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username));
if (slipNum != null){
// 设置失效时间为 5分钟
redisPlugin.expire(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username, LOGIN_PROPERTIES.getSlipLockSpeed());
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username),
LOGIN_PROPERTIES.getSlipLockSpeed());
// 如果确认 都失败 则存入临时缓存
if(slipNum >= LOGIN_PROPERTIES.getSlipCount()){
long currentTimeMillis = System.currentTimeMillis();
// 存入Redis
redisPlugin.put(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_LOCK_PREFIX + username,
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_LOCK + username),
currentTimeMillis, LOGIN_PROPERTIES.getSlipLockSpeed());
}
}
@ -396,7 +399,7 @@ public class UserTokenUtil {
long count = 0L;
Object obj = redisPlugin.get(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username));
if(obj != null){
try {
count = Convert.convert(Long.class, obj);
@ -418,10 +421,10 @@ public class UserTokenUtil {
// 删除失败次数记录
redisPlugin.del(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username));
// 删除失败次数记录
redisPlugin.del(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_LOCK_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_LOCK + username));
}

@ -29,16 +29,18 @@ import org.opsli.api.wrapper.system.menu.MenuModel;
import org.opsli.api.wrapper.system.role.RoleModel;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.api.wrapper.system.user.UserOrgRefModel;
import org.opsli.api.wrapper.system.user.UserOrgRefWebModel;
import org.opsli.common.constants.RedisConstants;
import org.opsli.common.exception.TokenException;
import org.opsli.core.api.TokenThreadLocal;
import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.msg.TokenMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@ -57,16 +59,6 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@Lazy(false)
public class UserUtil {
/** 前缀 */
public static final String PREFIX_ID = "userId:";
public static final String PREFIX_ID_ROLES = "userId:roles:";
public static final String PREFIX_ID_DEF_ROLE = "userId:def_role:";
public static final String PREFIX_ID_ORGS = "userId:orgs:";
public static final String PREFIX_ID_DEF_ORG = "userId:def_org:";
public static final String PREFIX_ID_PERMISSIONS = "userId:permissions:";
public static final String PREFIX_ID_MENUS = "userId:menus:";
public static final String PREFIX_USERNAME = "username:";
/** 修改租户权限 */
private static final String PERMS_TENANT = "system_user_tenant";
@ -85,6 +77,8 @@ public class UserUtil {
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
private static RedisTemplate<String, Object> redisTemplate;
/**
*
* @return UserModel
@ -169,67 +163,24 @@ public class UserUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_ID + userId;
// 先从缓存里拿
UserModel userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null){
// 如果不是递归触发 可进入一次
if(!isRecursion){
// 如果是 切换租户权限 则进行新一轮判断
// 注意不要陷入递归死循环 只保障循环一次
if (StringUtils.isNotBlank(userModel.getSwitchTenantUserId())){
userModel = getUser(userModel.getSwitchTenantUserId(), true);
}
}
return userModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null){
return userModel;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID + userId);
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
UserModel userModelTemp = new UserModel();
userModelTemp.setId(userId);
// 设置为系统内部调用 否则 会拿到 空值
userModelTemp.setIzApi(true);
// 查询数据库
ResultVo<UserModel> resultVo = userApi.get(userModelTemp);
if(resultVo.isSuccess()){
userModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, userModel);
if(!resultVo.isSuccess()){
return null;
}
return resultVo.getData();
}, true);
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(userModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
UserModel userModel = Convert.convert(UserModel.class, cache);
// 如果不是递归触发 可进入一次
if(!isRecursion){
@ -255,56 +206,18 @@ public class UserUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_USERNAME + userName;
// 先从缓存里拿
UserModel userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null){
return userModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null) {
return userModel;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USERNAME + userName);
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<UserModel> resultVo = userApi.getUserByUsername(userName);
if (resultVo.isSuccess()) {
userModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, userModel);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(userModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return resultVo.getData();
}, true);
return userModel;
return Convert.convert(UserModel.class, cache);
}
/**
@ -325,59 +238,22 @@ public class UserUtil {
}
// 缓存Key
String cacheKey = PREFIX_ID_ROLES + userId;
List<String> roles;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
roles = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(roles)){
return roles;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
roles = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(roles)){
return roles;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_AND_ROLES + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<String>> resultVo = userRoleRefApi.getRolesByUserId(userId);
if(resultVo.isSuccess()){
roles = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, roles);
ResultVo<List<String>> resultVo = userRoleRefApi.getRolesByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(roles)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<String> roles = Convert.toList(String.class, cache);
if(null == roles){
return ListUtil.empty();
}
return roles;
}
@ -399,60 +275,24 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_PERMISSIONS + userId;
List<String> permissions;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
permissions = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(permissions)){
return permissions;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
permissions = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(permissions)){
return permissions;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_PERMISSIONS + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<String>> resultVo = userRoleRefApi.getAllPerms(userId);
if(resultVo.isSuccess()){
permissions = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, permissions);
ResultVo<List<String>> resultVo = userRoleRefApi.getAllPerms(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(permissions)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<String> permissions = Convert.toList(String.class, cache);
if(null == permissions){
return ListUtil.empty();
}
return permissions;
}
@ -473,61 +313,24 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_ORGS + userId;
List<UserOrgRefModel> orgList;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
orgList = Convert.toList(UserOrgRefModel.class, obj);
if(CollUtil.isNotEmpty(orgList)){
return orgList;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
orgList = Convert.toList(UserOrgRefModel.class, obj);
if(CollUtil.isNotEmpty(orgList)){
return orgList;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_ORGS + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<UserOrgRefModel>> resultVo = userOrgRefApi.findListByUserId(userId);
if(resultVo.isSuccess()){
orgList = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, orgList);
ResultVo<List<UserOrgRefModel>> resultVo = userOrgRefApi.findListByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(orgList)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<UserOrgRefModel> orgList = Convert.toList(UserOrgRefModel.class, cache);
if(null == orgList){
return ListUtil.empty();
}
return orgList;
}
@ -561,61 +364,24 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_MENUS + userId;
List<MenuModel> menus;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
menus = Convert.toList(MenuModel.class, obj);
if(CollUtil.isNotEmpty(menus)){
return menus;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
menus = Convert.toList(MenuModel.class, obj);
if(CollUtil.isNotEmpty(menus)){
return menus;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_MENUS + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<MenuModel>> resultVo = userRoleRefApi.getMenuListByUserId(userId);
if(resultVo.isSuccess()){
menus = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, menus);
ResultVo<List<MenuModel>> resultVo = userRoleRefApi.getMenuListByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(menus)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<MenuModel> menus = Convert.toList(MenuModel.class, cache);
if(null == menus){
return ListUtil.empty();
}
return menus;
}
@ -637,57 +403,21 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_DEF_ROLE + userId;
// 先从缓存里拿
RoleModel roleModel = CacheUtil.getTimed(RoleModel.class, cacheKey);
if (roleModel != null){
return roleModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
roleModel = CacheUtil.getTimed(RoleModel.class, cacheKey);
if (roleModel != null){
return roleModel;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ROLE + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<RoleModel> resultVo = userRoleRefApi.getDefRoleByUserId(userId);
if(resultVo.isSuccess()){
roleModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, roleModel);
ResultVo<RoleModel> resultVo = userRoleRefApi.getDefRoleByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(roleModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return roleModel;
return Convert.convert(RoleModel.class, cache);
}
@ -710,56 +440,19 @@ public class UserUtil {
}
// 缓存Key
String cacheKey = PREFIX_ID_DEF_ORG + userId;
// 先从缓存里拿
UserOrgRefModel orgModel = CacheUtil.getTimed(UserOrgRefModel.class, cacheKey);
if (orgModel != null){
return orgModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
orgModel = CacheUtil.getTimed(UserOrgRefModel.class, cacheKey);
if (orgModel != null){
return orgModel;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ORG + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<UserOrgRefModel> resultVo = userOrgRefApi.getDefOrgByUserId(userId);
if(resultVo.isSuccess()){
orgModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, orgModel);
ResultVo<UserOrgRefModel> resultVo = userOrgRefApi.getDefOrgByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(orgModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return orgModel;
return Convert.convert(UserOrgRefModel.class, cache);
}
@ -781,33 +474,11 @@ public class UserUtil {
}
// 缓存Key
String cacheKey = PREFIX_ID + user.getId();
try {
// 存入缓存
CacheUtil.put(cacheKey, user);
}catch (Exception e){
log.error(e.getMessage(), e);
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return false;
}
// 存入缓存
flag = CacheUtil.put(cacheKey, user);
}catch (Exception e){
flag = false;
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID + user.getId());
// 存入缓存
SecurityCache.put(redisTemplate, cacheKey, user);
return flag;
return true;
}
/**
@ -824,55 +495,11 @@ public class UserUtil {
return true;
}
UserModel userModelById = CacheUtil.getTimed(
UserModel.class, PREFIX_ID + user.getId());
UserModel userModelByUsername = CacheUtil.getTimed(
UserModel.class, PREFIX_USERNAME + user.getUsername());
boolean hasNilFlagById = CacheUtil.hasNilFlag(PREFIX_ID + user.getId());
boolean hasNilFlagByName = CacheUtil.hasNilFlag(PREFIX_USERNAME + user.getUsername());
// 计数器
int count = 0;
if (hasNilFlagById){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID + user.getId());
if(tmp){
count--;
}
}
if (hasNilFlagByName){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_USERNAME + user.getUsername());
if(tmp){
count--;
}
}
// 只要有一个不为空 则执行刷新
if (userModelById != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID + user.getId());
if(tmp){
count--;
}
}
String cacheKeyByUserId = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID + user.getId());
String cacheKeyByUsername = CacheUtil.formatKey(RedisConstants.PREFIX_USERNAME + user.getUsername());
if (userModelByUsername != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_USERNAME + user.getUsername());
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.removeMore(redisTemplate,
cacheKeyByUserId, cacheKeyByUsername);
}
@ -886,32 +513,9 @@ public class UserUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
Object obj = CacheUtil.getTimed(PREFIX_ID_ROLES + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_ROLES + userId);
// 计数器
int count = 0;
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_ROLES + userId);
if(tmp){
count--;
}
}
if(obj != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_ROLES + userId);
if(tmp){
count--;
}
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_AND_ROLES + userId);
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -928,32 +532,9 @@ public class UserUtil {
return true;
}
// 计数器
int count = 0;
RoleModel roleModel = CacheUtil.getTimed(RoleModel.class, PREFIX_ID_DEF_ROLE + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_DEF_ROLE + userId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_DEF_ROLE + userId);
if(tmp){
count--;
}
}
if(roleModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_DEF_ROLE + userId);
if(tmp){
count--;
}
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ROLE + userId);
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
@ -968,34 +549,9 @@ public class UserUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_PERMISSIONS + userId);
Object obj = CacheUtil.getTimed(PREFIX_ID_PERMISSIONS + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_PERMISSIONS + userId);
// 计数器
int count = 0;
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_PERMISSIONS + userId);
if(tmp){
count--;
}
}
if(obj != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_PERMISSIONS + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -1012,31 +568,9 @@ public class UserUtil {
return true;
}
// 计数器
int count = 0;
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_ORGS + userId);
UserOrgRefWebModel orgRefModel = CacheUtil.getTimed(UserOrgRefWebModel.class, PREFIX_ID_ORGS + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_ORGS + userId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_ORGS + userId);
if(tmp){
count--;
}
}
if(orgRefModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_ORGS + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -1053,32 +587,9 @@ public class UserUtil {
return true;
}
// 计数器
int count = 0;
UserOrgRefModel orgModel = CacheUtil.getTimed(UserOrgRefModel.class, PREFIX_ID_DEF_ORG + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_DEF_ORG + userId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_DEF_ORG + userId);
if(tmp){
count--;
}
}
if(orgModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_DEF_ORG + userId);
if(tmp){
count--;
}
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ORG + userId);
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
@ -1092,33 +603,9 @@ public class UserUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_MENUS + userId);
Object obj = CacheUtil.getTimed(PREFIX_ID_MENUS + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_MENUS + userId);
// 计数器
int count = 0;
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_MENUS + userId);
if(tmp){
count--;
}
}
if(obj != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_MENUS + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -1201,7 +688,8 @@ public class UserUtil {
public void init(GlobalProperties globalProperties,
UserApi userApi,
UserRoleRefApi userRoleRefApi,
UserOrgRefApi userOrgRefApi){
UserOrgRefApi userOrgRefApi,
RedisTemplate<String, Object> redisTemplate) {
if(globalProperties != null && globalProperties.getAuth() != null
&& globalProperties.getAuth().getToken() != null
){
@ -1210,11 +698,9 @@ public class UserUtil {
}
UserUtil.userApi = userApi;
UserUtil.userRoleRefApi = userRoleRefApi;
UserUtil.userOrgRefApi = userOrgRefApi;
UserUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -40,7 +40,7 @@ import javax.servlet.http.HttpServletResponse;
*/
@Api(tags = GenLogsApi.TITLE)
@Slf4j
@ApiRestController("/generator/logs/{ver}")
@ApiRestController("/{ver}/generator/logs")
public class GenLogsRestController extends BaseRestController<GenLogs, GenLogsModel, IGenLogsService>
implements GenLogsApi {

@ -57,7 +57,7 @@ import java.util.Map;
*/
@Api(tags = TableApi.TITLE)
@Slf4j
@ApiRestController("/generator/table/{ver}")
@ApiRestController("/{ver}/generator/table")
public class GenTableRestController extends BaseRestController<GenTable, GenTableModel, IGenTableService>
implements TableApi {

@ -54,7 +54,7 @@ import org.opsli.modulars.generator.template.service.IGenTemplateDetailService;
*/
@Api(tags = GenTemplateDetailRestApi.TITLE)
@Slf4j
@ApiRestController("/generator/template/detail/{ver}")
@ApiRestController("/{ver}/generator/template/detail")
public class GenTemplateDetailRestController extends BaseRestController<GenTemplateDetail, GenTemplateDetailModel, IGenTemplateDetailService>
implements GenTemplateDetailRestApi {

@ -53,7 +53,7 @@ import org.opsli.modulars.generator.template.service.IGenTemplateService;
*/
@Api(tags = GenTemplateRestApi.TITLE)
@Slf4j
@ApiRestController("/generator/template/{ver}")
@ApiRestController("/{ver}/generator/template/")
public class GenTemplateRestController extends BaseRestController<GenTemplate, GenTemplateModel, IGenTemplateService>
implements GenTemplateRestApi {

@ -20,23 +20,18 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.web.system.dict.DictDetailApi;
import org.opsli.api.wrapper.system.dict.DictDetailModel;
import org.opsli.api.wrapper.system.dict.DictWrapper;
import org.opsli.common.constants.DictConstants;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.utils.DictUtil;
import org.opsli.core.utils.DistributedLockUtil;
import org.opsli.core.utils.ThrowExceptionUtil;
import org.opsli.modulars.generator.template.service.IGenTemplateDetailService;
import org.opsli.modulars.generator.template.wrapper.GenTemplateDetailModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@ -60,11 +55,13 @@ public class GenTemplateUtil {
private static boolean IS_INIT;
/** 缓存前缀 NAME */
private static final String CACHE_PREFIX_NAME = "gen:template:";
private static final String CACHE_PREFIX_NAME = "hash#{}:gen:template:";
/** 代码模板明细 Service */
private static IGenTemplateDetailService genTemplateDetailService;
private static RedisTemplate<String, Object> redisTemplate;
/**
* ID
* @param parentId ID
@ -76,77 +73,21 @@ public class GenTemplateUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = CACHE_PREFIX_NAME + parentId;
// 处理集合数据
List<GenTemplateDetailModel> wrapperModels = handleDictList(
CacheUtil.getHashAll(cacheKey), parentId);
if(CollUtil.isNotEmpty(wrapperModels)){
return sortWrappers(wrapperModels);
}
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return sortWrappers(wrapperModels);
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return sortWrappers(wrapperModels);
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
// 处理集合数据
wrapperModels = handleDictList(
CacheUtil.getHashAll(cacheKey), parentId);
if(CollUtil.isNotEmpty(wrapperModels)){
return sortWrappers(wrapperModels);
}
String cacheKey = CacheUtil.formatKey(CACHE_PREFIX_NAME + parentId);
List<GenTemplateDetailModel> listByParent = genTemplateDetailService.findListByParent(parentId);
Map<String, Object> templateCache = SecurityCache.hGetAll(redisTemplate, cacheKey, (k) -> {
// 处理数据库查询数据
if(CollUtil.isNotEmpty(listByParent)){
wrapperModels = listByParent;
// 计数器
int count = wrapperModels.size();
for (GenTemplateDetailModel model : wrapperModels) {
// 保存至缓存
boolean ret = GenTemplateUtil.put(model);
if(ret){
count--;
}
}
// 回滚 清空缓存
if(count != 0){
for (GenTemplateDetailModel model : wrapperModels) {
GenTemplateUtil.del(model);
}
}
return sortWrappers(wrapperModels);
List<GenTemplateDetailModel> listByParent = genTemplateDetailService.findListByParent(parentId);
Map<String, Object> templateMap = Maps.newHashMapWithExpectedSize(listByParent.size());
for (GenTemplateDetailModel genTemplateDetailModel : listByParent) {
templateMap.put(genTemplateDetailModel.getId(), genTemplateDetailModel);
}
return templateMap;
});
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
// 如果值还是 为空 则赋默认值
if(CollUtil.isEmpty(wrapperModels)){
// 加入缓存防穿透
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
}
// 排序
// 处理集合数据
List<GenTemplateDetailModel> wrapperModels = handleDictList(templateCache);
return sortWrappers(wrapperModels);
}
@ -157,8 +98,8 @@ public class GenTemplateUtil {
*/
private static List<GenTemplateDetailModel> sortWrappers(List<GenTemplateDetailModel> wrapperModels) {
// 非法判读
if(wrapperModels == null){
return null;
if(CollUtil.isEmpty(wrapperModels)){
return ListUtil.empty();
}
return ListUtil.sort(wrapperModels,
@ -169,22 +110,6 @@ public class GenTemplateUtil {
// ===============
/**
*
* @param model
*/
private static boolean put(GenTemplateDetailModel model){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 清除缓存
GenTemplateUtil.del(model);
return CacheUtil.putHash(CACHE_PREFIX_NAME + model.getParentId(),
model.getId(), model);
}
/**
*
* @param model
@ -199,36 +124,10 @@ public class GenTemplateUtil {
return true;
}
boolean hasNilFlag = CacheUtil.hasNilFlag(CACHE_PREFIX_NAME +
model.getParentId() + ":" + model.getId());
GenTemplateDetailModel templateDetailModel = CacheUtil.getHash(GenTemplateDetailModel.class,
CACHE_PREFIX_NAME + model.getParentId(),
model.getId());
// 计数器
int count = 0;
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(CACHE_PREFIX_NAME +
model.getParentId() + ":" + model.getId());
if(tmp){
count--;
}
}
if (templateDetailModel != null){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delHash(CACHE_PREFIX_NAME +
model.getParentId(), model.getId());
if(tmp){
count--;
}
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(CACHE_PREFIX_NAME + model.getParentId());
return count == 0;
return SecurityCache.hDel(redisTemplate, cacheKey, model.getId());
}
/**
@ -241,29 +140,18 @@ public class GenTemplateUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
List<GenTemplateDetailModel> wrapperList = GenTemplateUtil.getTemplateDetailList(parentId);
if(CollUtil.isEmpty(wrapperList)){
return true;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(CACHE_PREFIX_NAME + parentId);
// 计数器
int count = wrapperList.size();
for (GenTemplateDetailModel wrapperModel : wrapperList) {
boolean tmp = GenTemplateUtil.del(wrapperModel);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/***
*
* @param tempMap Map
* @param id ID
* @return List
*/
public static List<GenTemplateDetailModel> handleDictList(Map<String, Object> tempMap, String id){
public static List<GenTemplateDetailModel> handleDictList(Map<String, Object> tempMap){
List<GenTemplateDetailModel> wrapperModels = Lists.newArrayList();
if(CollUtil.isNotEmpty(tempMap)){
for (Map.Entry<String, Object> entry : tempMap.entrySet()) {
@ -285,8 +173,10 @@ public class GenTemplateUtil {
*
*/
@Autowired
public void init(IGenTemplateDetailService genTemplateDetailService) {
public void init(IGenTemplateDetailService genTemplateDetailService,
RedisTemplate<String, Object> redisTemplate) {
GenTemplateUtil.genTemplateDetailService = genTemplateDetailService;
GenTemplateUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -71,7 +71,7 @@ import java.util.Set;
*/
@Api(tags = SysAreaRestApi.TITLE)
@Slf4j
@ApiRestController("/system/area/{ver}")
@ApiRestController("/{ver}/system/area")
public class SysAreaRestController extends BaseRestController<SysArea, SysAreaModel, ISysAreaService>
implements SysAreaRestApi {

@ -56,7 +56,7 @@ import java.util.List;
*/
@Api(tags = DictDetailApi.TITLE)
@Slf4j
@ApiRestController("/system/dict/detail/{ver}")
@ApiRestController("/{ver}/system/dict/detail")
public class DictDetailRestController extends BaseRestController<SysDictDetail, DictDetailModel, IDictDetailService>
implements DictDetailApi {

@ -59,7 +59,7 @@ import java.util.List;
*/
@Api(tags = DictApi.TITLE)
@Slf4j
@ApiRestController("/system/dict/{ver}")
@ApiRestController("/{ver}/system/dict")
public class DictRestController extends BaseRestController<SysDict, DictModel, IDictService>
implements DictApi {

@ -26,7 +26,7 @@ import org.opsli.api.wrapper.system.menu.MenuModel;
import org.opsli.api.wrapper.system.options.OptionsModel;
import org.opsli.api.wrapper.system.tenant.TenantModel;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.annotation.ApiCryptoAsymmetric;
import org.opsli.common.annotation.LoginCrypto;
import org.opsli.common.annotation.Limiter;
import org.opsli.common.enums.DictType;
import org.opsli.common.thread.AsyncProcessExecutor;
@ -73,7 +73,7 @@ public class LoginRestController {
*
*/
@Limiter
@ApiCryptoAsymmetric(responseEncrypt = false)
@LoginCrypto
@ApiOperation(value = "登录", notes = "登录")
@PostMapping("/system/login")
public ResultVo<UserTokenUtil.TokenRet> login(@RequestBody LoginForm form, HttpServletRequest request){

@ -42,7 +42,7 @@ import javax.servlet.http.HttpServletRequest;
*/
@Api(tags = LogsApi.TITLE)
@Slf4j
@ApiRestController("/system/logs/{ver}")
@ApiRestController("/{ver}/system/logs")
public class LogsRestController extends BaseRestController<SysLogs, LogsModel, ILogsService>
implements LogsApi {

@ -73,7 +73,7 @@ import java.util.Map;
*/
@Api(tags = MenuApi.TITLE)
@Slf4j
@ApiRestController("/system/menu/{ver}")
@ApiRestController("/{ver}/system/menu")
public class MenuRestController extends BaseRestController<SysMenu, MenuModel, IMenuService>
implements MenuApi {

@ -35,7 +35,7 @@ import java.util.Map;
*/
@Api(tags = "系统监控")
@Slf4j
@ApiRestController("/system/monitor/{ver}")
@ApiRestController("/{ver}/system/monitor")
public class MonitorController {
/**

@ -60,7 +60,7 @@ import java.util.Map;
*/
@Api(tags = OptionsApi.TITLE)
@Slf4j
@ApiRestController("/system/options/{ver}")
@ApiRestController("/{ver}/system/options")
public class SysOptionsRestController extends BaseRestController<SysOptions, OptionsModel, ISysOptionsService>
implements OptionsApi {

@ -73,7 +73,7 @@ import java.util.Set;
*/
@Api(tags = SysOrgRestApi.TITLE)
@Slf4j
@ApiRestController("/system/org/{ver}")
@ApiRestController("/{ver}/system/org")
public class SysOrgRestController extends BaseRestController<SysOrg, SysOrgModel, ISysOrgService>
implements SysOrgRestApi {

@ -48,7 +48,7 @@ import java.util.stream.Collectors;
*/
@Api(tags = RoleMenuRefApi.TITLE)
@Slf4j
@ApiRestController("/system/role/perms/{ver}")
@ApiRestController("/{ver}/system/role/perms")
public class RoleMenuRefRestController implements RoleMenuRefApi {
/** 配置类 */

@ -49,7 +49,7 @@ import java.lang.reflect.Method;
*/
@Api(tags = "角色管理")
@Slf4j
@ApiRestController("/system/role/{ver}")
@ApiRestController("/{ver}/system/role")
public class RoleRestController extends BaseRestController<SysRole, RoleModel, IRoleService>
implements RoleApi {

@ -52,7 +52,7 @@ import java.lang.reflect.Method;
*/
@Api(tags = TenantApi.TITLE)
@Slf4j
@ApiRestController("/system/tenant/{ver}")
@ApiRestController("/{ver}/system/tenant")
public class TenantRestController extends BaseRestController<SysTenant, TenantModel, ITenantService>
implements TenantApi {

@ -54,7 +54,7 @@ import java.util.List;
*/
@Api(tags = UserOrgRefApi.TITLE)
@Slf4j
@ApiRestController("/system/user/org/{ver}")
@ApiRestController("/{ver}/system/user/org")
public class UserOrgRefRestController implements UserOrgRefApi {
/** 配置类 */

@ -17,6 +17,7 @@ package org.opsli.modulars.system.user.web;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.util.CollectionUtils;
@ -61,6 +62,7 @@ import org.opsli.plugins.oss.OssStorageFactory;
import org.opsli.plugins.oss.service.BaseOssStorageService;
import org.opsli.plugins.oss.service.OssStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -80,7 +82,7 @@ import java.util.List;
*/
@Api(tags = UserApi.TITLE)
@Slf4j
@ApiRestController("/system/user/{ver}")
@ApiRestController("/{ver}/system/user")
public class UserRestController extends BaseRestController<SysUser, UserModel, IUserService>
implements UserApi {
@ -215,10 +217,14 @@ public class UserRestController extends BaseRestController<SysUser, UserModel, I
}
try {
MultipartFile multipartFile = files.get(0);
Resource resource = multipartFile.getResource();
String filename = resource.getFilename();
// 调用OSS 服务保存头像
OssStorageService ossStorageService = OssStorageFactory.INSTANCE.getHandle();
BaseOssStorageService.FileAttr fileAttr = ossStorageService.upload(
files.get(0).getInputStream(), "jpg");
multipartFile.getInputStream(), FileUtil.extName(filename));
UserModel user = UserUtil.getUserBySource();
// 更新头像至数据库

@ -50,7 +50,7 @@ import java.util.List;
*/
@Api(tags = UserRoleRefApi.TITLE)
@Slf4j
@ApiRestController("/system/user/roles/{ver}")
@ApiRestController("/{ver}/system/user/roles")
public class UserRoleRefRestController implements UserRoleRefApi {
/** 配置类 */

@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.GetMapping;
* @date 2021102712:50:00
*/
@Api(tags = "API-测试版本控制")
@ApiRestController("/tools/api/{ver}")
@ApiRestController("/{ver}/tools/api")
public class ApiController {
@ApiOperation(value = "测试正常接口", notes = "测试正常接口")

@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestBody;
*/
@Api(tags = "邮件类")
@Slf4j
@ApiRestController("/tools/email/{ver}")
@ApiRestController("/{ver}/tools/email")
public class EmailRestController {
@Autowired

@ -13,7 +13,7 @@ import org.opsli.common.annotation.ApiRestController;
*/
@Api(tags = "文件管理类")
@Slf4j
@ApiRestController("/tools/oss/{ver}")
@ApiRestController("/{ver}/tools/oss")
public class OssRestController {

@ -36,7 +36,7 @@ import java.util.Set;
*/
@Api(tags = "搜索历史记录")
@Slf4j
@ApiRestController("/tools/searchhis/{ver}")
@ApiRestController("/{ver}/tools/searchhis")
public class SearchHisRestController {
/**

@ -47,7 +47,7 @@ import org.opsli.api.web.gentest.carinfo.TestCarRestApi;
*/
@Api(tags = TestCarRestApi.TITLE)
@Slf4j
@ApiRestController("/gentest/carinfo/{ver}")
@ApiRestController("/{ver}/gentest/carinfo")
public class TestCarRestController extends BaseRestController<TestCar, TestCarModel, ITestCarService>
implements TestCarRestApi {

@ -48,7 +48,7 @@ import java.lang.reflect.Method;
*/
@Api(tags = TestUserRestApi.TITLE)
@Slf4j
@ApiRestController("/gentest/user/{ver}")
@ApiRestController("/{ver}/gentest/user")
public class TestUserRestController extends BaseRestController<TestUser, TestUserModel, ITestUserService>
implements TestUserRestApi {

@ -32,7 +32,7 @@ import java.lang.reflect.Method;
*/
@Api(tags = "测试类")
@Slf4j
@ApiRestController("/test/{ver}")
@ApiRestController("/{ver}/test")
public class TestRestController extends BaseRestController<TestEntity, TestModel, ITestService>
implements TestRestApi {

@ -18,8 +18,12 @@ package opsli.plugins.crypto.strategy.impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.DES;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONUtil;
@ -245,11 +249,11 @@ public class CryptoSymmetricServiceImpl implements CryptoSymmetricService {
byte[] keyBytes = Base64.decode(model.getPrivateKey());
switch (model.getCryptoType()) {
case AES:{
tmp = SecureUtil.aes(keyBytes);
tmp = new AES(Mode.ECB, Padding.PKCS5Padding, keyBytes);
break;
}
case DES:{
tmp = SecureUtil.des(keyBytes);
tmp = new DES(Mode.ECB, Padding.PKCS5Padding, keyBytes);
break;
}
case DE_SEDE:{

@ -1,10 +1,4 @@
{
"properties": [
{
"name": "spring.redis.pushsub.enable",
"type": "java.lang.Boolean",
"defaultValue": false,
"description": "开启消息订阅."
}
]
}

@ -11,7 +11,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>opsli-starter</artifactId>
<version>1.6.0</version>
<version>1.6.2</version>
<dependencies>

@ -54,21 +54,11 @@ spring:
# allow-bean-definition-overriding: true
# 缓存配置项
cache-conf:
cache:
# 前缀
prefix: opsli
# 一级缓存 ---- EhCache 配置
cache:
# 是否启用本地缓存 (默认不启用, 如果业务对于缓存依赖较高可启用本地缓存作为一级缓存)
enable: false
# 加载配置文件
jcache:
config: classpath:config/ehcache-opsli.xml
# 二级缓存 ---- Redis 配置
redis:
# 开启消息订阅
pushsub:
enable: true
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
@ -163,8 +153,8 @@ opsli:
url-patterns: "/*"
# 排除过滤URL
url-exclusion:
- "/api/generator/template/v1/insertAndDetail"
- "/api/generator/template/v1/updateAndDetail"
- "/api/v1/generator/template/insertAndDetail"
- "/api/v1/generator/template/updateAndDetail"
- "/static/files/"
- "/doc.html"

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">
<!--指定缓存持久化目录-->
<persistence directory="${java.io.tmpdir}/opsli-ehcache-data"/>
<!-- <cache-template>可以让你创建一个抽象的<cache>配置文件,该配置文件可以进一步的被扩展。-->
<!-- 默认缓存模版 -->
<cache-template name="opsliDefaults">
<!--键值对被声明为字符串类型如果没有指明默认是Object类型。-->
<key-type>java.lang.String</key-type>
<value-type>java.lang.Object</value-type>
<resources>
<heap unit="entries">2000</heap> <!--配置堆储存-->
<!-- <offheap unit="MB">300</offheap> &lt;!&ndash;配置堆外储存&ndash;&gt;-->
</resources>
</cache-template>
<!-- timed 时效数据它使用名为opsliDefaults的<cache-template>,并将其主键覆盖到更广泛的类型 -->
<cache alias="timed" uses-template="opsliDefaults">
<!--缓存到期配置-->
<expiry>
<!-- 只允许配秒 默认 21600秒6小时 -->
<ttl unit="seconds">21600</ttl>
</expiry>
</cache>
</config>

@ -2,8 +2,8 @@
echo "Git Pushing ......"
echo ""
git push "git@github.com:hiparker/opsli-boot.git" master
git push -f "git@gitee.com:hiparker/opsli-boot.git" master
git push "git@github.com:hiparker/opsli-boot.git" development
git push -f "git@gitee.com:hiparker/opsli-boot.git" development

@ -2,8 +2,8 @@
echo "Git Pushing ......"
echo ""
git push "git@github.com:hiparker/opsli-boot.git" master
git push -f "git@gitee.com:hiparker/opsli-boot.git" master
git push "git@github.com:hiparker/opsli-boot.git" development
git push -f "git@gitee.com:hiparker/opsli-boot.git" development

Loading…
Cancel
Save