1. 新建租户、修改租户、修改租户套餐时,自动修改角色的权限
2. 租户的本地缓存,提升访问性能 3. 精简本地缓存的实现逻辑
This commit is contained in:
parent
4d53944771
commit
e4be51b14a
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
|
|||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BinaryOperator;
|
||||
|
@ -125,6 +126,15 @@ public class CollectionUtils {
|
|||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();
|
||||
from.forEach(item -> builder.put(keyFunc.apply(item), item));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
||||
}
|
||||
|
@ -140,6 +150,15 @@ public class CollectionUtils {
|
|||
return from.stream().filter(predicate).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getMaxValue(List<T> from, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
assert from.size() > 0; // 断言,避免告警
|
||||
T t = from.stream().max(Comparator.comparing(valueFunc)).get();
|
||||
return valueFunc.apply(t);
|
||||
}
|
||||
|
||||
public static <T> void addIfNotNull(Collection<T> coll, T item) {
|
||||
if (item == null) {
|
||||
return;
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
|
|
|
@ -6,15 +6,16 @@ import cn.hutool.json.JSONUtil;
|
|||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelCreateReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelExportReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelPageReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.merchant.PayChannelMapper;
|
||||
import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
|
||||
|
@ -27,12 +28,12 @@ import javax.annotation.PostConstruct;
|
|||
import javax.annotation.Resource;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 支付渠道 Service 实现类
|
||||
|
@ -66,9 +67,10 @@ public class PayChannelServiceImpl implements PayChannelService {
|
|||
|
||||
@Override
|
||||
@PostConstruct
|
||||
@TenantIgnore // 忽略自动化租户,全局初始化本地缓存
|
||||
public void initPayClients() {
|
||||
// 获取支付渠道,如果有更新
|
||||
List<PayChannelDO> payChannels = this.loadPayChannelIfUpdate(maxUpdateTime);
|
||||
List<PayChannelDO> payChannels = loadPayChannelIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(payChannels)) {
|
||||
return;
|
||||
}
|
||||
|
@ -78,8 +80,7 @@ public class PayChannelServiceImpl implements PayChannelService {
|
|||
payChannel.getCode(), payChannel.getConfig()));
|
||||
|
||||
// 写入缓存
|
||||
assert payChannels.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = payChannels.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(payChannels, PayChannelDO::getUpdateTime);
|
||||
log.info("[initPayClients][初始化 PayChannel 数量为 {}]", payChannels.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public class AuthController {
|
|||
// 获得角色列表
|
||||
List<RoleDO> roleList = roleService.getRolesFromCache(getLoginUserRoleIds());
|
||||
// 获得菜单列表
|
||||
List<MenuDO> menuList = permissionService.getRoleMenusFromCache(
|
||||
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(
|
||||
getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它
|
||||
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
|
||||
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus()));
|
||||
|
@ -84,7 +84,7 @@ public class AuthController {
|
|||
@ApiOperation("获得登录用户的菜单列表")
|
||||
public CommonResult<List<AuthMenuRespVO>> getMenus() {
|
||||
// 获得用户拥有的菜单列表
|
||||
List<MenuDO> menuList = permissionService.getRoleMenusFromCache(
|
||||
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(
|
||||
getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它
|
||||
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
|
||||
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
|
||||
|
|
|
@ -55,7 +55,7 @@ public class MenuController {
|
|||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperation("获取菜单列表")
|
||||
@ApiOperation(value = "获取菜单列表", notes = "用于【菜单管理】界面")
|
||||
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
||||
public CommonResult<List<MenuRespVO>> getMenus(MenuListReqVO reqVO) {
|
||||
List<MenuDO> list = menuService.getMenus(reqVO);
|
||||
|
@ -64,13 +64,13 @@ public class MenuController {
|
|||
}
|
||||
|
||||
@GetMapping("/list-all-simple")
|
||||
@ApiOperation(value = "获取菜单精简信息列表", notes = "只包含被开启的菜单,主要用于前端的下拉选项")
|
||||
@ApiOperation(value = "获取菜单精简信息列表", notes = "只包含被开启的菜单,用于【角色分配菜单】功能的选项")
|
||||
public CommonResult<List<MenuSimpleRespVO>> getSimpleMenus() {
|
||||
// 获得菜单列表,只要开启状态的
|
||||
MenuListReqVO reqVO = new MenuListReqVO();
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
List<MenuDO> list = menuService.getMenus(reqVO);
|
||||
// 排序后,返回个诶前端
|
||||
// 排序后,返回给前端
|
||||
list.sort(Comparator.comparing(MenuDO::getSort));
|
||||
return success(MenuConvert.INSTANCE.convertList02(list));
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public class PermissionController {
|
|||
@GetMapping("/list-role-resources")
|
||||
// @RequiresPermissions("system:permission:assign-role-menu")
|
||||
public CommonResult<Set<Long>> listRoleMenus(Long roleId) {
|
||||
return success(permissionService.listRoleMenuIds(roleId));
|
||||
return success(permissionService.getRoleMenuIds(roleId));
|
||||
}
|
||||
|
||||
@PostMapping("/assign-role-menu")
|
||||
|
|
|
@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModelProperty;
|
|||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 租户套餐 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
|
@ -26,6 +26,6 @@ public class TenantPackageBaseVO {
|
|||
|
||||
@ApiModelProperty(value = "关联的菜单编号", required = true)
|
||||
@NotNull(message = "关联的菜单编号不能为空")
|
||||
private List<Long> menuIds;
|
||||
private Set<Long> menuIds;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
|
||||
import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
|
@ -21,7 +21,7 @@ import java.util.Set;
|
|||
@TableName(value = "system_role", autoResultMap = true)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RoleDO extends BaseDO {
|
||||
public class RoleDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
@ -14,7 +15,7 @@ import lombok.EqualsAndHashCode;
|
|||
@TableName("system_role_menu")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RoleMenuDO extends BaseDO {
|
||||
public class RoleMenuDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
||||
|
@ -41,7 +42,7 @@ public interface RoleMapper extends BaseMapperX<RoleDO> {
|
|||
}
|
||||
|
||||
default List<RoleDO> selectListByStatus(@Nullable Collection<Integer> statuses) {
|
||||
return selectList(new QueryWrapperX<RoleDO>().in("status", statuses));
|
||||
return selectList(new LambdaQueryWrapperX<RoleDO>().inIfPresent(RoleDO::getStatus, statuses));
|
||||
}
|
||||
|
||||
@InterceptorIgnore(tenantLine = "true") // 该方法忽略多租户。原因:该方法被异步 task 调用,此时获取不到租户编号
|
||||
|
|
|
@ -7,7 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -43,7 +45,14 @@ public interface TenantMapper extends BaseMapperX<TenantDO> {
|
|||
}
|
||||
|
||||
default Integer selectCountByPackageId(Long packageId) {
|
||||
return selectCount("package_id", packageId);
|
||||
return selectCount(TenantDO::getPackageId, packageId);
|
||||
}
|
||||
|
||||
default List<TenantDO> selectListByPackageId(Long packageId) {
|
||||
return selectList(TenantDO::getPackageId, packageId);
|
||||
}
|
||||
|
||||
@Select("SELECT id FROM system_tenant WHERE update_time > #{maxUpdateTime} LIMIT 1")
|
||||
Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
|
||||
|
||||
}
|
||||
|
|
|
@ -10,12 +10,17 @@ import lombok.Getter;
|
|||
@AllArgsConstructor
|
||||
public enum RoleCodeEnum {
|
||||
|
||||
ADMIN("admin"), // 超级管理员
|
||||
SUPER_ADMIN("super_admin", "超级管理员"),
|
||||
TENANT_ADMIN("tenant_admin", "租户管理员"),
|
||||
;
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
private final String key;
|
||||
private final String code;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import javax.annotation.Resource;
|
|||
* 针对 {@link SmsSendMessage} 的消费者
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/3/9 16:35
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.system.mq.consumer.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.module.system.mq.message.tenant.TenantRefreshMessage;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 针对 {@link cn.iocoder.yudao.module.system.mq.message.tenant.TenantRefreshMessage} 的消费者
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class TenantRefreshConsumer extends AbstractChannelMessageListener<TenantRefreshMessage> {
|
||||
|
||||
@Resource
|
||||
private TenantService tenantService;
|
||||
|
||||
@Override
|
||||
public void onMessage(TenantRefreshMessage message) {
|
||||
log.info("[onMessage][收到 Tenant 刷新消息]");
|
||||
tenantService.initLocalCache();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cn.iocoder.yudao.module.system.mq.message.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 租户数据刷新 Message
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TenantRefreshMessage extends AbstractChannelMessage {
|
||||
|
||||
@Override
|
||||
public String getChannel() {
|
||||
return "system.tenant.refresh";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.system.mq.producer.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
|
||||
import cn.iocoder.yudao.module.system.mq.message.permission.RoleRefreshMessage;
|
||||
import cn.iocoder.yudao.module.system.mq.message.tenant.TenantRefreshMessage;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Tenant 租户相关消息的 Producer
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class TenantProducer {
|
||||
|
||||
@Resource
|
||||
private RedisMQTemplate redisMQTemplate;
|
||||
|
||||
/**
|
||||
* 发送 {@link RoleRefreshMessage} 消息
|
||||
*/
|
||||
public void sendTenantRefreshMessage() {
|
||||
TenantRefreshMessage message = new TenantRefreshMessage();
|
||||
redisMQTemplate.send(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
|||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
|
||||
|
@ -73,9 +73,10 @@ public class DeptServiceImpl implements DeptService {
|
|||
|
||||
@Override
|
||||
@PostConstruct
|
||||
@TenantIgnore // 初始化缓存,无需租户过滤
|
||||
public synchronized void initLocalCache() {
|
||||
// 获取部门列表,如果有更新
|
||||
List<DeptDO> deptList = this.loadDeptIfUpdate(maxUpdateTime);
|
||||
List<DeptDO> deptList = loadDeptIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(deptList)) {
|
||||
return;
|
||||
}
|
||||
|
@ -90,8 +91,7 @@ public class DeptServiceImpl implements DeptService {
|
|||
// 设置缓存
|
||||
deptCache = builder.build();
|
||||
parentDeptCache = parentBuilder.build();
|
||||
assert deptList.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = deptList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(deptList, DeptDO::getUpdateTime);
|
||||
log.info("[initLocalCache][初始化 Dept 数量为 {}]", deptList.size());
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ public class DeptServiceImpl implements DeptService {
|
|||
* @param maxUpdateTime 当前部门的最大更新时间
|
||||
* @return 部门列表
|
||||
*/
|
||||
private List<DeptDO> loadDeptIfUpdate(Date maxUpdateTime) {
|
||||
protected List<DeptDO> loadDeptIfUpdate(Date maxUpdateTime) {
|
||||
// 第一步,判断是否要更新。
|
||||
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||
log.info("[loadMenuIfUpdate][首次加载全量部门]");
|
||||
|
@ -118,7 +118,7 @@ public class DeptServiceImpl implements DeptService {
|
|||
log.info("[loadMenuIfUpdate][增量加载全量部门]");
|
||||
}
|
||||
// 第二步,如果有更新,则从数据库加载所有部门
|
||||
return deptMapper.selectListIgnoreTenant();
|
||||
return deptMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.service.dict;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
|
||||
|
@ -99,8 +99,7 @@ public class DictDataServiceImpl implements DictDataService {
|
|||
});
|
||||
labelDictDataCache = labelDictDataBuilder.build();
|
||||
valueDictDataCache = valueDictDataBuilder.build();
|
||||
assert dataList.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(dataList, DictDataDO::getUpdateTime);
|
||||
log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public interface MenuService {
|
|||
* @param menusStatuses 菜单状态数组
|
||||
* @return 菜单列表
|
||||
*/
|
||||
List<MenuDO> listMenusFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses);
|
||||
List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses);
|
||||
|
||||
/**
|
||||
* 获得指定编号的菜单数组,从缓存中
|
||||
|
@ -78,7 +78,7 @@ public interface MenuService {
|
|||
* @param menusStatuses 菜单状态数组
|
||||
* @return 菜单数组
|
||||
*/
|
||||
List<MenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||
List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses);
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.service.permission;
|
|||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO;
|
||||
|
@ -12,7 +12,6 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper;
|
|||
import cn.iocoder.yudao.module.system.enums.permission.MenuIdEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
|
@ -95,8 +94,7 @@ public class MenuServiceImpl implements MenuService {
|
|||
});
|
||||
menuCache = menuCacheBuilder.build();
|
||||
permissionMenuCache = permMenuCacheBuilder.build();
|
||||
assert menuList.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(menuList, MenuDO::getUpdateTime);
|
||||
log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
|
||||
}
|
||||
|
||||
|
@ -201,7 +199,7 @@ public class MenuServiceImpl implements MenuService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<MenuDO> listMenusFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses) {
|
||||
public List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses) {
|
||||
// 任一一个参数为空,则返回空
|
||||
if (CollectionUtils.isAnyEmpty(menuTypes, menusStatuses)) {
|
||||
return Collections.emptyList();
|
||||
|
@ -213,7 +211,7 @@ public class MenuServiceImpl implements MenuService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<MenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||
public List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses) {
|
||||
// 任一一个参数为空,则返回空
|
||||
if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {
|
||||
|
|
|
@ -33,7 +33,7 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
|
|||
* @param menusStatuses 菜单状态数组
|
||||
* @return 菜单列表
|
||||
*/
|
||||
List<MenuDO> getRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||
List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses);
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
|
|||
* @param roleId 角色编号
|
||||
* @return 菜单编号集合
|
||||
*/
|
||||
Set<Long> listRoleMenuIds(Long roleId);
|
||||
Set<Long> getRoleMenuIds(Long roleId);
|
||||
|
||||
/**
|
||||
* 设置角色菜单
|
||||
|
|
|
@ -3,6 +3,14 @@ package cn.iocoder.yudao.module.system.service.permission;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
|
@ -12,13 +20,6 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
|
|||
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
|
||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -94,10 +95,10 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
*/
|
||||
@Override
|
||||
@PostConstruct
|
||||
@TenantIgnore // 初始化缓存,无需租户过滤
|
||||
public void initLocalCache() {
|
||||
Date now = new Date();
|
||||
// 获取角色与菜单的关联列表,如果有更新
|
||||
List<RoleMenuDO> roleMenuList = this.loadRoleMenuIfUpdate(maxUpdateTime);
|
||||
List<RoleMenuDO> roleMenuList = loadRoleMenuIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(roleMenuList)) {
|
||||
return;
|
||||
}
|
||||
|
@ -111,8 +112,7 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
});
|
||||
roleMenuCache = roleMenuCacheBuilder.build();
|
||||
menuRoleCache = menuRoleCacheBuilder.build();
|
||||
assert roleMenuList.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = now;
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(roleMenuList, RoleMenuDO::getUpdateTime);
|
||||
log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
* @param maxUpdateTime 当前角色与菜单的关联的最大更新时间
|
||||
* @return 角色与菜单的关联列表
|
||||
*/
|
||||
private List<RoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) {
|
||||
protected List<RoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) {
|
||||
// 第一步,判断是否要更新。
|
||||
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||
log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]");
|
||||
|
@ -143,21 +143,22 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<MenuDO> getRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||
public List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses) {
|
||||
// 任一一个参数为空时,不返回任何菜单
|
||||
if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 判断角色是否包含管理员
|
||||
|
||||
// 判断角色是否包含超级管理员。如果是超级管理员,获取到全部
|
||||
List<RoleDO> roleList = roleService.getRolesFromCache(roleIds);
|
||||
boolean hasAdmin = roleService.hasAnyAdmin(roleList);
|
||||
// 获得角色拥有的菜单关联
|
||||
if (hasAdmin) { // 管理员,获取到全部
|
||||
return menuService.listMenusFromCache(menuTypes, menusStatuses);
|
||||
if (roleService.hasAnySuperAdmin(roleList)) {
|
||||
return menuService.getMenuListFromCache(menuTypes, menusStatuses);
|
||||
}
|
||||
|
||||
// 获得角色拥有的菜单关联
|
||||
List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);
|
||||
return menuService.listMenusFromCache(menuIds, menuTypes, menusStatuses);
|
||||
return menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -174,10 +175,10 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> listRoleMenuIds(Long roleId) {
|
||||
public Set<Long> getRoleMenuIds(Long roleId) {
|
||||
// 如果是管理员的情况下,获取全部菜单编号
|
||||
RoleDO role = roleService.getRole(roleId);
|
||||
if (roleService.hasAnyAdmin(Collections.singletonList(role))) {
|
||||
if (roleService.hasAnySuperAdmin(Collections.singletonList(role))) {
|
||||
return CollectionUtils.convertSet(menuService.getMenus(), MenuDO::getId);
|
||||
}
|
||||
// 如果是非管理员的情况下,获得拥有的菜单编号
|
||||
|
@ -302,7 +303,7 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
return false;
|
||||
}
|
||||
// 判断是否是超管。如果是,当然符合条件
|
||||
if (roleService.hasAnyAdmin(roleIds)) {
|
||||
if (roleService.hasAnySuperAdmin(roleIds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -337,7 +338,7 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
return false;
|
||||
}
|
||||
// 判断是否是超管。如果是,当然符合条件
|
||||
if (roleService.hasAnyAdmin(roleIds)) {
|
||||
if (roleService.hasAnySuperAdmin(roleIds)) {
|
||||
return true;
|
||||
}
|
||||
Set<String> userRoles = CollectionUtils.convertSet(roleService.getRolesFromCache(roleIds),
|
||||
|
|
|
@ -90,12 +90,12 @@ public interface RoleService {
|
|||
List<RoleDO> getRolesFromCache(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 判断角色数组中,是否有管理员
|
||||
* 判断角色数组中,是否有超级管理员
|
||||
*
|
||||
* @param roleList 角色数组
|
||||
* @return 是否有管理员
|
||||
*/
|
||||
boolean hasAnyAdmin(Collection<RoleDO> roleList);
|
||||
boolean hasAnySuperAdmin(Collection<RoleDO> roleList);
|
||||
|
||||
/**
|
||||
* 判断角色编号数组中,是否有管理员
|
||||
|
@ -103,8 +103,8 @@ public interface RoleService {
|
|||
* @param ids 角色编号数组
|
||||
* @return 是否有管理员
|
||||
*/
|
||||
default boolean hasAnyAdmin(Set<Long> ids) {
|
||||
return hasAnyAdmin(getRolesFromCache(ids));
|
||||
default boolean hasAnySuperAdmin(Set<Long> ids) {
|
||||
return hasAnySuperAdmin(getRolesFromCache(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,8 +6,8 @@ import cn.hutool.core.util.ObjectUtil;
|
|||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
||||
|
@ -19,7 +19,6 @@ import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
|||
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
@ -78,19 +77,17 @@ public class RoleServiceImpl implements RoleService {
|
|||
*/
|
||||
@Override
|
||||
@PostConstruct
|
||||
@TenantIgnore // 忽略自动多租户,全局初始化缓存
|
||||
public void initLocalCache() {
|
||||
// 获取角色列表,如果有更新
|
||||
List<RoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime);
|
||||
List<RoleDO> roleList = loadRoleIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(roleList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
ImmutableMap.Builder<Long, RoleDO> builder = ImmutableMap.builder();
|
||||
roleList.forEach(sysRoleDO -> builder.put(sysRoleDO.getId(), sysRoleDO));
|
||||
roleCache = builder.build();
|
||||
assert roleList.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = roleList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId);
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime);
|
||||
log.info("[initLocalCache][初始化 Role 数量为 {}]", roleList.size());
|
||||
}
|
||||
|
||||
|
@ -216,11 +213,11 @@ public class RoleServiceImpl implements RoleService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyAdmin(Collection<RoleDO> roleList) {
|
||||
public boolean hasAnySuperAdmin(Collection<RoleDO> roleList) {
|
||||
if (CollectionUtil.isEmpty(roleList)) {
|
||||
return false;
|
||||
}
|
||||
return roleList.stream().anyMatch(roleDO -> RoleCodeEnum.ADMIN.getKey().equals(roleDO.getCode()));
|
||||
return roleList.stream().anyMatch(roleDO -> RoleCodeEnum.SUPER_ADMIN.getCode().equals(roleDO.getCode()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package cn.iocoder.yudao.module.system.service.sms;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
|
||||
|
@ -8,10 +12,6 @@ import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
|
|||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -19,13 +19,12 @@ import org.springframework.stereotype.Service;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
|
||||
/**
|
||||
* 短信渠道Service实现类
|
||||
|
@ -74,8 +73,7 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
|||
propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
|
||||
|
||||
// 写入缓存
|
||||
assert smsChannels.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = smsChannels.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(smsChannels, SmsChannelDO::getUpdateTime);
|
||||
log.info("[initSmsClients][初始化 SmsChannel 数量为 {}]", smsChannels.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -3,24 +3,23 @@ package cn.iocoder.yudao.module.system.service.sms;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.sms.SmsTemplateConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -31,8 +30,8 @@ import javax.annotation.Resource;
|
|||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 短信模板 Service 实现类
|
||||
|
@ -89,11 +88,8 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
|
|||
}
|
||||
|
||||
// 写入缓存
|
||||
ImmutableMap.Builder<String, SmsTemplateDO> builder = ImmutableMap.builder();
|
||||
smsTemplateList.forEach(sysSmsTemplateDO -> builder.put(sysSmsTemplateDO.getCode(), sysSmsTemplateDO));
|
||||
smsTemplateCache = builder.build();
|
||||
assert smsTemplateList.size() > 0; // 断言,避免告警
|
||||
maxUpdateTime = smsTemplateList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
||||
smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode);
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(smsTemplateList, SmsTemplateDO::getUpdateTime);
|
||||
log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package cn.iocoder.yudao.module.system.service.tenant;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
@ -45,12 +48,18 @@ public class TenantPackageServiceImpl implements TenantPackageService {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
this.validateTenantPackageExists(updateReqVO.getId());
|
||||
TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId());
|
||||
// 更新
|
||||
TenantPackageDO updateObj = TenantPackageConvert.INSTANCE.convert(updateReqVO);
|
||||
tenantPackageMapper.updateById(updateObj);
|
||||
// 如果菜单发生变化,则修改每个租户的菜单
|
||||
if (!CollUtil.isEqualList(tenantPackage.getMenuIds(), updateReqVO.getMenuIds())) {
|
||||
List<TenantDO> tenants = tenantService.getTenantListByPackageId(tenantPackage.getId());
|
||||
tenants.forEach(tenant -> tenantService.updateTenantRoleMenu(tenant.getId(), updateReqVO.getMenuIds()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,10 +72,12 @@ public class TenantPackageServiceImpl implements TenantPackageService {
|
|||
tenantPackageMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateTenantPackageExists(Long id) {
|
||||
if (tenantPackageMapper.selectById(id) == null) {
|
||||
private TenantPackageDO validateTenantPackageExists(Long id) {
|
||||
TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id);
|
||||
if (tenantPackage == null) {
|
||||
throw exception(TENANT_PACKAGE_NOT_EXISTS);
|
||||
}
|
||||
return tenantPackage;
|
||||
}
|
||||
|
||||
private void validateTenantUsed(Long id) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
|||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 租户 Service 接口
|
||||
|
@ -19,6 +20,11 @@ import java.util.List;
|
|||
*/
|
||||
public interface TenantService extends TenantFrameworkService {
|
||||
|
||||
/**
|
||||
* 初始化租户的本地缓存
|
||||
*/
|
||||
void initLocalCache();
|
||||
|
||||
/**
|
||||
* 创建租户
|
||||
*
|
||||
|
@ -34,6 +40,14 @@ public interface TenantService extends TenantFrameworkService {
|
|||
*/
|
||||
void updateTenant(@Valid TenantUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新租户的角色菜单
|
||||
*
|
||||
* @param tenantId 租户编号
|
||||
* @param menuIds 菜单编号数组
|
||||
*/
|
||||
void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds);
|
||||
|
||||
/**
|
||||
* 删除租户
|
||||
*
|
||||
|
@ -89,4 +103,12 @@ public interface TenantService extends TenantFrameworkService {
|
|||
*/
|
||||
Integer getTenantCountByPackageId(Long packageId);
|
||||
|
||||
/**
|
||||
* 获得使用指定套餐的租户数组
|
||||
*
|
||||
* @param packageId 租户套餐编号
|
||||
* @return 租户数组
|
||||
*/
|
||||
List<TenantDO> getTenantListByPackageId(Long packageId);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package cn.iocoder.yudao.module.system.service.tenant;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
|
@ -11,21 +14,27 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantEx
|
|||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||
|
@ -37,8 +46,27 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
|||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TenantServiceImpl implements TenantService {
|
||||
|
||||
/**
|
||||
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期
|
||||
* 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
|
||||
*/
|
||||
private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* 角色缓存
|
||||
* key:角色编号 {@link RoleDO#getId()}
|
||||
*
|
||||
* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
|
||||
*/
|
||||
private volatile Map<Long, TenantDO> tenantCache;
|
||||
/**
|
||||
* 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
|
||||
*/
|
||||
private volatile Date maxUpdateTime;
|
||||
|
||||
@Resource
|
||||
private TenantMapper tenantMapper;
|
||||
|
||||
|
@ -51,15 +79,61 @@ public class TenantServiceImpl implements TenantService {
|
|||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private TenantProducer tenantProducer;
|
||||
|
||||
/**
|
||||
* 初始化 {@link #tenantCache} 缓存
|
||||
*/
|
||||
@Override
|
||||
@PostConstruct
|
||||
public void initLocalCache() {
|
||||
// 获取租户列表,如果有更新
|
||||
List<TenantDO> tenantList = loadTenantIfUpdate(maxUpdateTime);
|
||||
if (CollUtil.isEmpty(tenantList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
tenantCache = CollectionUtils.convertImmutableMap(tenantList, TenantDO::getId);
|
||||
maxUpdateTime = CollectionUtils.getMaxValue(tenantList, TenantDO::getUpdateTime);
|
||||
log.info("[initLocalCache][初始化 Tenant 数量为 {}]", tenantList.size());
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
|
||||
public void schedulePeriodicRefresh() {
|
||||
initLocalCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果租户发生变化,从数据库中获取最新的全量租户。
|
||||
* 如果未发生变化,则返回空
|
||||
*
|
||||
* @param maxUpdateTime 当前租户的最大更新时间
|
||||
* @return 租户列表
|
||||
*/
|
||||
private List<TenantDO> loadTenantIfUpdate(Date maxUpdateTime) {
|
||||
// 第一步,判断是否要更新。
|
||||
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||
log.info("[loadTenantIfUpdate][首次加载全量租户]");
|
||||
} else { // 判断数据库中是否有更新的租户
|
||||
if (tenantMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
|
||||
return null;
|
||||
}
|
||||
log.info("[loadTenantIfUpdate][增量加载全量租户]");
|
||||
}
|
||||
// 第二步,如果有更新,则从数据库加载所有租户
|
||||
return tenantMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getTenantIds() {
|
||||
List<TenantDO> tenants = tenantMapper.selectList();
|
||||
return CollectionUtils.convertList(tenants, TenantDO::getId);
|
||||
return new ArrayList<>(tenantCache.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validTenant(Long id) {
|
||||
TenantDO tenant = tenantMapper.selectById(id);
|
||||
TenantDO tenant = tenantCache.get(id);
|
||||
if (tenant == null) {
|
||||
throw exception(TENANT_NOT_EXISTS);
|
||||
}
|
||||
|
@ -75,7 +149,7 @@ public class TenantServiceImpl implements TenantService {
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createTenant(TenantCreateReqVO createReqVO) {
|
||||
// 校验套餐被禁用
|
||||
tenantPackageService.validTenantPackage(createReqVO.getPackageId());
|
||||
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
|
||||
|
||||
// 创建租户
|
||||
TenantDO tenant = TenantConvert.INSTANCE.convert(createReqVO);
|
||||
|
@ -83,13 +157,19 @@ public class TenantServiceImpl implements TenantService {
|
|||
|
||||
TenantUtils.execute(tenant.getId(), () -> {
|
||||
// 创建角色
|
||||
Long roleId = createRole();
|
||||
Long roleId = createRole(tenantPackage);
|
||||
// 创建用户,并分配角色
|
||||
Long userId = createUser(roleId, createReqVO);
|
||||
// 修改租户的管理员
|
||||
tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
|
||||
});
|
||||
// 返回
|
||||
// 发送刷新消息
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
tenantProducer.sendTenantRefreshMessage();
|
||||
}
|
||||
});
|
||||
return tenant.getId();
|
||||
}
|
||||
|
||||
|
@ -101,36 +181,80 @@ public class TenantServiceImpl implements TenantService {
|
|||
return userId;
|
||||
}
|
||||
|
||||
private Long createRole() {
|
||||
private Long createRole(TenantPackageDO tenantPackage) {
|
||||
// 创建角色
|
||||
RoleCreateReqVO reqVO = new RoleCreateReqVO();
|
||||
reqVO.setName(RoleCodeEnum.ADMIN.name()).setCode(RoleCodeEnum.ADMIN.getKey()).setSort(0);
|
||||
return roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
|
||||
reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())
|
||||
.setSort(0).setRemark("系统自动生成");
|
||||
Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
|
||||
// 分配权限
|
||||
permissionService.assignRoleMenu(roleId, tenantPackage.getMenuIds());
|
||||
return roleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateTenant(TenantUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
this.validateTenantExists(updateReqVO.getId());
|
||||
TenantDO tenant = validateTenantExists(updateReqVO.getId());
|
||||
// 校验套餐被禁用
|
||||
tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
|
||||
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
|
||||
|
||||
// 更新
|
||||
// 更新租户
|
||||
TenantDO updateObj = TenantConvert.INSTANCE.convert(updateReqVO);
|
||||
tenantMapper.updateById(updateObj);
|
||||
// 如果套餐发生变化,则修改其角色的权限
|
||||
if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) {
|
||||
updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds());
|
||||
}
|
||||
// 发送刷新消息
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
tenantProducer.sendTenantRefreshMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
|
||||
TenantUtils.execute(tenantId, () -> {
|
||||
// 获得所有角色
|
||||
List<RoleDO> roles = roleService.getRoles(null);
|
||||
roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配",
|
||||
role.getId(), role.getTenantId(), tenantId)); // 兜底校验
|
||||
// 重新分配每个角色的权限
|
||||
roles.forEach(role -> {
|
||||
// 如果是租户管理员,重新分配其权限为租户套餐的权限
|
||||
if (Objects.equals(role.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
|
||||
permissionService.assignRoleMenu(role.getId(), menuIds);
|
||||
log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), menuIds);
|
||||
return;
|
||||
}
|
||||
// 如果是其他角色,则去掉超过套餐的权限
|
||||
Set<Long> roleMenuIds = permissionService.getRoleMenuIds(role.getId());
|
||||
roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds);
|
||||
permissionService.assignRoleMenu(role.getId(), roleMenuIds);
|
||||
log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTenant(Long id) {
|
||||
// 校验存在
|
||||
this.validateTenantExists(id);
|
||||
validateTenantExists(id);
|
||||
// 删除
|
||||
tenantMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateTenantExists(Long id) {
|
||||
if (tenantMapper.selectById(id) == null) {
|
||||
private TenantDO validateTenantExists(Long id) {
|
||||
TenantDO tenant = tenantMapper.selectById(id);
|
||||
if (tenant == null) {
|
||||
throw exception(TENANT_NOT_EXISTS);
|
||||
}
|
||||
return tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -163,4 +287,9 @@ public class TenantServiceImpl implements TenantService {
|
|||
return tenantMapper.selectCountByPackageId(packageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TenantDO> getTenantListByPackageId(Long packageId) {
|
||||
return tenantMapper.selectListByPackageId(packageId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -243,7 +243,7 @@ public class MenuServiceTest extends BaseDbUnitTest {
|
|||
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
|
||||
mockCacheMap.put(menuDO.getId(), menuDO);
|
||||
|
||||
List<MenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(MenuTypeEnum.MENU.getType()),
|
||||
List<MenuDO> menuDOS = sysMenuService.getMenuListFromCache(Collections.singletonList(MenuTypeEnum.MENU.getType()),
|
||||
Collections.singletonList(CommonStatusEnum.DISABLE.getStatus()));
|
||||
assertEquals(menuDOS.size(), idMenuMap.size());
|
||||
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
|
||||
|
@ -270,7 +270,7 @@ public class MenuServiceTest extends BaseDbUnitTest {
|
|||
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
|
||||
mockCacheMap.put(menuDO.getId(), menuDO);
|
||||
|
||||
List<MenuDO> menuDOS = sysMenuService.listMenusFromCache(Collections.singletonList(1L),
|
||||
List<MenuDO> menuDOS = sysMenuService.getMenuListFromCache(Collections.singletonList(1L),
|
||||
Collections.singletonList(MenuTypeEnum.MENU.getType()), Collections.singletonList(1));
|
||||
assertEquals(menuDOS.size(), idMenuMap.size());
|
||||
menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu));
|
||||
|
|
|
@ -79,8 +79,8 @@ yudao:
|
|||
- cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants
|
||||
tenant: # 多租户相关配置项
|
||||
enable: true
|
||||
ignore-urls: /admin-api/system/captcha/get-image, /admin-api/infra/file/get/*
|
||||
ignore-tables: infra_config, infra_file, infra_job, infra_job_log, infra_job_log, system_tenant, system_tenant_package, system_dict_data, system_dict_type, system_error_code, system_menu, system_sms_channel, tool_codegen_column, tool_codegen_table, tool_test_demo, tables, columns
|
||||
ignore-urls: /admin-api/system/tenant/get-id-by-name, /admin-api/system/captcha/get-image, /admin-api/infra/file/get/*
|
||||
ignore-tables: infra_config, infra_file, infra_job, infra_job_log, infra_job_log, system_tenant, system_tenant_package, system_dict_data, system_dict_type, system_error_code, system_menu, system_sms_channel, system_sms_template, tool_codegen_column, tool_codegen_table, tool_test_demo, tables, columns
|
||||
sms-code: # 短信验证码相关的配置项
|
||||
expire-times: 10m
|
||||
send-frequency: 1m
|
||||
|
|
|
@ -200,6 +200,8 @@ export default {
|
|||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加租户套餐";
|
||||
// 设置为非严格,继续使用半选中
|
||||
this.menuCheckStrictly = false;
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
|
@ -222,12 +224,6 @@ export default {
|
|||
/** 获得菜单 */
|
||||
getMenus() {
|
||||
listSimpleMenus().then(response => {
|
||||
// 移除 BUTTON 类型,暂支只需要发 DIR、MENU 类型
|
||||
for (let i = response.data.length -1; i >= 0 ; i--) {
|
||||
if (response.data[i].type === SystemMenuTypeEnum.BUTTON) {
|
||||
response.data.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// 处理 menuOptions 参数
|
||||
this.menuOptions = [];
|
||||
// 只需要配置
|
||||
|
|
3
更新日志.md
3
更新日志.md
|
@ -29,8 +29,9 @@ TODO
|
|||
* 【新增】后端 `yudao.tenant.enable` 配置项,前端 `VUE_APP_TENANT_ENABLE` 配置项,用于开关租户功能。 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/79311ecc71f0c6beabe0e5f84e1423ce745a5f09)
|
||||
* 【优化】调整默认所有表开启多租户的特性,可通过 `yudao.tenant.ignore-tables` 配置项进行忽略,替代原本默认不开启的策略 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/79311ecc71f0c6beabe0e5f84e1423ce745a5f09)
|
||||
* 【新增】通过 `yudao.tenant.ignore-urls` 配置忽略多租户的请求,例如说 ,例如说短信回调、支付回调等 Open API [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/79311ecc71f0c6beabe0e5f84e1423ce745a5f09)
|
||||
* 【新增】新增 `@TenantIgnore` 注解,标记指定方法,忽略多租户的自动过滤,适合实现跨租户的逻辑 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/4d53944771c66b563da1e3d68d3ba43405af8a06)
|
||||
* 【新增】租户套餐的管理,可配置每个租户的可使用的功能权限 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/6b6d676a6baa2dad16ae9bf03d5002209064c8cc)
|
||||
* 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 []()
|
||||
* 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/2598c033a95d4b61d5f5ab3da5f1414f25c510d6)
|
||||
|
||||
### 🐞 Bug Fixes
|
||||
|
||||
|
|
Loading…
Reference in New Issue