优化租户,加入租户缓存提高登录效率

v1.4.1
Parker 4 years ago
parent 9a915a8941
commit fb0d9214e3

@ -125,4 +125,13 @@ public interface TenantApi {
@GetMapping("/importExcel/template")
void importTemplate(HttpServletResponse response);
// =========================
/**
*
* @param tenantId
* @return ResultVo
*/
@GetMapping("/getTenantByUsable")
ResultVo<TenantModel> getTenantByUsable(String tenantId);
}

@ -52,6 +52,11 @@ public enum MsgArgsType {
/** 组织 用户数据 */
ORG_USER_DATA,
/** 租户ID */
TENANT_ID,
/** 租户数据 */
TENANT_DATA,
/** 缓存数据Key */
CACHE_DATA_KEY,
/** 缓存数据Value */

@ -36,6 +36,9 @@ public enum PushSubType {
/** 组织数据 */
ORG,
/** 租户 */
TENANT,
/** 热点数据 */
HOT_DATA,

@ -0,0 +1,78 @@
/**
* 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.TenantUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @BelongsProject: opsli-boot
* @BelongsPackage: org.opsli.core.cache.pushsub.handler
* @Author: Parker
* @CreateTime: 2020-09-15 16:24
* @Description:
*/
@Slf4j
public class TenantHandler implements RedisPushSubHandler{
@Autowired
EhCachePlugin ehCachePlugin;
@Override
public PushSubType getType() {
return PushSubType.TENANT;
}
@Override
public void handler(JSONObject msgJson) {
// 用户刷新
this.orgHandler(msgJson);
}
/**
*
* @param msgJson
*/
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;
}
// 先删除
ehCachePlugin.delete(CacheConstants.HOT_DATA, TenantUtil.PREFIX_CODE + tenantId);
// 清除空拦截
CacheUtil.delNilFlag(TenantUtil.PREFIX_CODE + tenantId);
}
}

@ -0,0 +1,61 @@
/**
* 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.api.wrapper.system.user.UserOrgRefModel;
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;
/**
* @BelongsProject: opsli-boot
* @BelongsPackage: org.opsli.core.cache.pushsub.msgs
* @Author: Parker
* @CreateTime: 2020-09-15 16:50
* @Description:
*/
@Data
@Accessors(chain = true)
public final class TenantMsgFactory extends BaseSubMessage{
/** 通道 */
private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL;
private TenantMsgFactory(){}
/**
* -
*/
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;
}
}

@ -0,0 +1,185 @@
/**
* 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.utils;
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.api.wrapper.system.user.UserOrgRefModel;
import org.opsli.common.exception.TokenException;
import org.opsli.core.cache.local.CacheUtil;
import org.opsli.core.cache.pushsub.msgs.OrgMsgFactory;
import org.opsli.core.cache.pushsub.msgs.TenantMsgFactory;
import org.opsli.core.msg.TokenMsg;
import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
import org.opsli.plugins.redis.RedisLockPlugins;
import org.opsli.plugins.redis.RedisPlugin;
import org.opsli.plugins.redis.lock.RedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.List;
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
/**
* @BelongsProject: opsli-boot
* @BelongsPackage: org.opsli.core.utils
* @Author: Parker
* @CreateTime: 2020-09-19 20:03
* @Description:
*/
@Slf4j
@Order(UTIL_ORDER)
@Component
@AutoConfigureAfter({RedisPlugin.class , RedisLockPlugins.class, TenantApi.class})
@Lazy(false)
public class TenantUtil {
/** 前缀 */
public static final String PREFIX_CODE = "tenant:id:";
/** Redis插件 */
private static RedisPlugin redisPlugin;
/** Redis分布式锁 */
private static RedisLockPlugins redisLockPlugins;
/** 租户 Api */
private static TenantApi tenantApi;
/**
* tenantId
* @param tenantId
* @return
*/
public static TenantModel getTenant(String tenantId){
// 先从缓存里拿
TenantModel tenantModel = CacheUtil.get(PREFIX_CODE + tenantId, TenantModel.class);
if (tenantModel != null){
return tenantModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_CODE + tenantId);
if(hasNilFlag){
return null;
}
// 锁凭证 redisLock 贯穿全程
RedisLock redisLock = new RedisLock();
redisLock.setLockName(PREFIX_CODE + tenantId)
.setAcquireTimeOut(3000L)
.setLockTimeOut(5000L);
try {
// 这里增加分布式锁 防止缓存击穿
// ============ 尝试加锁
redisLock = redisLockPlugins.tryLock(redisLock);
if(redisLock == null){
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
tenantModel = CacheUtil.get(PREFIX_CODE + tenantId, TenantModel.class);
if (tenantModel != null){
return tenantModel;
}
// 查询数据库
ResultVo<TenantModel> resultVo = tenantApi.getTenantByUsable(tenantId);
if(resultVo.isSuccess()){
tenantModel = resultVo.getData();
// 存入缓存
CacheUtil.put(PREFIX_CODE + tenantId, tenantModel);
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// ============ 释放锁
redisLockPlugins.unLock(redisLock);
}
if(tenantModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(PREFIX_CODE + tenantId);
return null;
}
return tenantModel;
}
// ============== 刷新缓存 ==============
/**
* -
* @param tenantId
* @return
*/
public static void refreshTenant(String tenantId){
if(StringUtils.isEmpty(tenantId)){
return;
}
TenantModel tenantModel = CacheUtil.get(PREFIX_CODE + tenantId, TenantModel.class);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_CODE + tenantId);
// 只要不为空 则执行刷新
if (hasNilFlag){
// 清除空拦截
CacheUtil.delNilFlag(PREFIX_CODE + tenantId);
}
if(tenantModel != null){
// 先删除
CacheUtil.del(PREFIX_CODE + tenantId);
// 发送通知消息
redisPlugin.sendMessage(
TenantMsgFactory.createTenantMsg(tenantModel)
);
}
}
// =====================================
@Autowired
public void setRedisPlugin(RedisPlugin redisPlugin) {
TenantUtil.redisPlugin = redisPlugin;
}
@Autowired
public void setRedisLockPlugins(RedisLockPlugins redisLockPlugins) {
TenantUtil.redisLockPlugins = redisLockPlugins;
}
@Autowired
public void setTenantApi(TenantApi tenantApi) {
TenantUtil.tenantApi = tenantApi;
}
}

@ -156,6 +156,7 @@ public class DictDetailServiceImpl extends CrudServiceImpl<DictDetailMapper, Sys
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(String id) {
DictDetailModel dictModel = this.get(id);
boolean ret = super.delete(id);
@ -187,6 +188,7 @@ public class DictDetailServiceImpl extends CrudServiceImpl<DictDetailMapper, Sys
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(DictDetailModel model) {
DictDetailModel dictModel = this.get(model);
boolean ret = super.delete(model);
@ -218,6 +220,7 @@ public class DictDetailServiceImpl extends CrudServiceImpl<DictDetailMapper, Sys
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteAll(String[] ids) {
QueryBuilder<SysDictDetail> queryBuilder = new GenQueryBuilder<>();
QueryWrapper<SysDictDetail> queryWrapper = queryBuilder.build();
@ -263,6 +266,7 @@ public class DictDetailServiceImpl extends CrudServiceImpl<DictDetailMapper, Sys
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteAll(Collection<DictDetailModel> models) {
QueryBuilder<SysDictDetail> queryBuilder = new GenQueryBuilder<>();

@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.wrapper.system.tenant.TenantModel;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.api.TokenThreadLocal;
import org.opsli.common.exception.TokenException;
@ -31,6 +32,7 @@ import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
import org.opsli.core.persistence.querybuilder.QueryBuilder;
import org.opsli.core.security.shiro.realm.OAuth2Realm;
import org.opsli.core.utils.CaptchaUtil;
import org.opsli.core.utils.TenantUtil;
import org.opsli.core.utils.UserTokenUtil;
import org.opsli.core.utils.UserUtil;
import org.opsli.modulars.system.login.entity.LoginForm;
@ -113,12 +115,8 @@ public class LoginRestController {
// 如果不是超级管理员 需要验证租户是否生效
if(!UserUtil.SUPER_ADMIN.equals(user.getUsername())){
QueryBuilder<SysTenant> queryBuilder = new GenQueryBuilder<>();
QueryWrapper<SysTenant> queryWrapper = queryBuilder.build();
queryWrapper.eq("id", user.getTenantId())
.eq("iz_usable", "1");
List<SysTenant> tenants = iTenantService.findList(queryWrapper);
if(tenants == null || tenants.isEmpty()){
TenantModel tenant = TenantUtil.getTenant(user.getTenantId());
if(tenant == null){
throw new TokenException(TokenMsg.EXCEPTION_LOGIN_TENANT_NOT_USABLE);
}
}

@ -15,9 +15,18 @@
*/
package org.opsli.modulars.system.tenant.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import org.opsli.api.wrapper.system.tenant.TenantModel;
import org.opsli.common.constants.MyBatisConstants;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.utils.HumpUtil;
import org.opsli.core.base.service.impl.CrudServiceImpl;
import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
import org.opsli.core.persistence.querybuilder.QueryBuilder;
import org.opsli.core.utils.TenantUtil;
import org.opsli.modulars.system.SystemMsg;
import org.opsli.modulars.system.tenant.entity.SysTenant;
import org.opsli.modulars.system.tenant.mapper.TenantMapper;
@ -26,6 +35,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* @BelongsProject: opsli-boot
@ -73,7 +85,108 @@ public class TenantServiceImpl extends CrudServiceImpl<TenantMapper, SysTenant,
throw new ServiceException(SystemMsg.EXCEPTION_TENANT_UNIQUE);
}
return super.update(model);
TenantModel tenantModel = super.update(model);
if(tenantModel != null){
// 刷新缓存
TenantUtil.refreshTenant(tenantModel.getId());
}
return tenantModel;
}
/**
*
* @param id ID
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(String id) {
TenantModel tenantModel = this.get(id);
boolean ret = super.delete(id);
if(ret){
// 刷新缓存
TenantUtil.refreshTenant(tenantModel.getId());
}
return ret;
}
/**
*
* @param model
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(TenantModel model) {
TenantModel tenantModel = this.get(model);
boolean ret = super.delete(model);
if(ret){
// 刷新缓存
TenantUtil.refreshTenant(tenantModel.getId());
}
return ret;
}
/**
* -
* @param ids id
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteAll(String[] ids) {
List<String> idList = Convert.toList(String.class, ids);
QueryBuilder<SysTenant> queryBuilder = new GenQueryBuilder<>();
QueryWrapper<SysTenant> queryWrapper = queryBuilder.build();
queryWrapper.in(HumpUtil.humpToUnderline(MyBatisConstants.FIELD_ID), idList);
List<SysTenant> list = this.findList(queryWrapper);
boolean ret = super.deleteAll(ids);
if(ret){
if(CollUtil.isNotEmpty(list)){
for (SysTenant sysTenant : list) {
// 刷新缓存
TenantUtil.refreshTenant(sysTenant.getId());
}
}
}
return ret;
}
/**
* -
* @param models
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteAll(Collection<TenantModel> models) {
List<String> idList = Lists.newArrayListWithCapacity(models.size());
for (TenantModel model : models) {
idList.add(model.getId());
}
QueryBuilder<SysTenant> queryBuilder = new GenQueryBuilder<>();
QueryWrapper<SysTenant> queryWrapper = queryBuilder.build();
queryWrapper.in(HumpUtil.humpToUnderline(MyBatisConstants.FIELD_ID), idList);
List<SysTenant> list = this.findList(queryWrapper);
boolean ret = super.deleteAll(models);
if(ret){
if(CollUtil.isNotEmpty(list)){
for (SysTenant sysTenant : list) {
// 刷新缓存
TenantUtil.refreshTenant(sysTenant.getId());
}
}
}
return ret;
}

@ -16,8 +16,11 @@
package org.opsli.modulars.system.tenant.web;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.web.system.role.RoleApi;
@ -26,8 +29,10 @@ import org.opsli.api.wrapper.system.tenant.TenantModel;
import org.opsli.common.annotation.ApiRestController;
import org.opsli.common.annotation.EnableLog;
import org.opsli.common.annotation.RequiresPermissionsCus;
import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.base.concroller.BaseRestController;
import org.opsli.core.persistence.Page;
import org.opsli.core.persistence.querybuilder.GenQueryBuilder;
import org.opsli.core.persistence.querybuilder.QueryBuilder;
import org.opsli.core.persistence.querybuilder.WebQueryBuilder;
import org.opsli.modulars.system.tenant.entity.SysTenant;
@ -202,4 +207,25 @@ public class TenantRestController extends BaseRestController<SysTenant, TenantMo
super.importTemplate(RoleApi.TITLE, response, method);
}
// =========================
/**
*
* @param tenantId
* @return ResultVo
*/
@ApiOperation(value = "获得已启用租户", notes = "获得已启用租户 - ID")
@Override
public ResultVo<TenantModel> getTenantByUsable(String tenantId) {
QueryBuilder<SysTenant> queryBuilder = new GenQueryBuilder<>();
QueryWrapper<SysTenant> queryWrapper = queryBuilder.build();
queryWrapper.eq("id", tenantId)
.eq("iz_usable", "1");
SysTenant entity = IService.getOne(queryWrapper);
return ResultVo.success(
WrapperUtil.transformInstance(entity, TenantModel.class)
);
}
}

Loading…
Cancel
Save