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.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
|
@ -125,6 +126,15 @@ public class CollectionUtils {
|
||||||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
|
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) {
|
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||||
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
||||||
}
|
}
|
||||||
|
@ -140,6 +150,15 @@ public class CollectionUtils {
|
||||||
return from.stream().filter(predicate).findFirst().orElse(null);
|
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) {
|
public static <T> void addIfNotNull(Collection<T> coll, T item) {
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -33,6 +33,10 @@
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
|
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Web 相关 -->
|
<!-- Web 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -6,15 +6,16 @@ import cn.hutool.json.JSONUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.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.PayChannelCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelExportReqVO;
|
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.PayChannelPageReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelUpdateReqVO;
|
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.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.dataobject.merchant.PayChannelDO;
|
||||||
import cn.iocoder.yudao.module.pay.dal.mysql.merchant.PayChannelMapper;
|
import cn.iocoder.yudao.module.pay.dal.mysql.merchant.PayChannelMapper;
|
||||||
import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
|
import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
|
||||||
|
@ -27,12 +28,12 @@ import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.validation.Validator;
|
import javax.validation.Validator;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
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 实现类
|
* 支付渠道 Service 实现类
|
||||||
|
@ -66,9 +67,10 @@ public class PayChannelServiceImpl implements PayChannelService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@TenantIgnore // 忽略自动化租户,全局初始化本地缓存
|
||||||
public void initPayClients() {
|
public void initPayClients() {
|
||||||
// 获取支付渠道,如果有更新
|
// 获取支付渠道,如果有更新
|
||||||
List<PayChannelDO> payChannels = this.loadPayChannelIfUpdate(maxUpdateTime);
|
List<PayChannelDO> payChannels = loadPayChannelIfUpdate(maxUpdateTime);
|
||||||
if (CollUtil.isEmpty(payChannels)) {
|
if (CollUtil.isEmpty(payChannels)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -78,8 +80,7 @@ public class PayChannelServiceImpl implements PayChannelService {
|
||||||
payChannel.getCode(), payChannel.getConfig()));
|
payChannel.getCode(), payChannel.getConfig()));
|
||||||
|
|
||||||
// 写入缓存
|
// 写入缓存
|
||||||
assert payChannels.size() > 0; // 断言,避免告警
|
maxUpdateTime = CollectionUtils.getMaxValue(payChannels, PayChannelDO::getUpdateTime);
|
||||||
maxUpdateTime = payChannels.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initPayClients][初始化 PayChannel 数量为 {}]", payChannels.size());
|
log.info("[initPayClients][初始化 PayChannel 数量为 {}]", payChannels.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class AuthController {
|
||||||
// 获得角色列表
|
// 获得角色列表
|
||||||
List<RoleDO> roleList = roleService.getRolesFromCache(getLoginUserRoleIds());
|
List<RoleDO> roleList = roleService.getRolesFromCache(getLoginUserRoleIds());
|
||||||
// 获得菜单列表
|
// 获得菜单列表
|
||||||
List<MenuDO> menuList = permissionService.getRoleMenusFromCache(
|
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(
|
||||||
getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它
|
getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它
|
||||||
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
|
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
|
||||||
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus()));
|
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
|
@ -84,7 +84,7 @@ public class AuthController {
|
||||||
@ApiOperation("获得登录用户的菜单列表")
|
@ApiOperation("获得登录用户的菜单列表")
|
||||||
public CommonResult<List<AuthMenuRespVO>> getMenus() {
|
public CommonResult<List<AuthMenuRespVO>> getMenus() {
|
||||||
// 获得用户拥有的菜单列表
|
// 获得用户拥有的菜单列表
|
||||||
List<MenuDO> menuList = permissionService.getRoleMenusFromCache(
|
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(
|
||||||
getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它
|
getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它
|
||||||
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
|
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
|
||||||
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
|
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class MenuController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@ApiOperation("获取菜单列表")
|
@ApiOperation(value = "获取菜单列表", notes = "用于【菜单管理】界面")
|
||||||
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
||||||
public CommonResult<List<MenuRespVO>> getMenus(MenuListReqVO reqVO) {
|
public CommonResult<List<MenuRespVO>> getMenus(MenuListReqVO reqVO) {
|
||||||
List<MenuDO> list = menuService.getMenus(reqVO);
|
List<MenuDO> list = menuService.getMenus(reqVO);
|
||||||
|
@ -64,13 +64,13 @@ public class MenuController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/list-all-simple")
|
@GetMapping("/list-all-simple")
|
||||||
@ApiOperation(value = "获取菜单精简信息列表", notes = "只包含被开启的菜单,主要用于前端的下拉选项")
|
@ApiOperation(value = "获取菜单精简信息列表", notes = "只包含被开启的菜单,用于【角色分配菜单】功能的选项")
|
||||||
public CommonResult<List<MenuSimpleRespVO>> getSimpleMenus() {
|
public CommonResult<List<MenuSimpleRespVO>> getSimpleMenus() {
|
||||||
// 获得菜单列表,只要开启状态的
|
// 获得菜单列表,只要开启状态的
|
||||||
MenuListReqVO reqVO = new MenuListReqVO();
|
MenuListReqVO reqVO = new MenuListReqVO();
|
||||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
List<MenuDO> list = menuService.getMenus(reqVO);
|
List<MenuDO> list = menuService.getMenus(reqVO);
|
||||||
// 排序后,返回个诶前端
|
// 排序后,返回给前端
|
||||||
list.sort(Comparator.comparing(MenuDO::getSort));
|
list.sort(Comparator.comparing(MenuDO::getSort));
|
||||||
return success(MenuConvert.INSTANCE.convertList02(list));
|
return success(MenuConvert.INSTANCE.convertList02(list));
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class PermissionController {
|
||||||
@GetMapping("/list-role-resources")
|
@GetMapping("/list-role-resources")
|
||||||
// @RequiresPermissions("system:permission:assign-role-menu")
|
// @RequiresPermissions("system:permission:assign-role-menu")
|
||||||
public CommonResult<Set<Long>> listRoleMenus(Long roleId) {
|
public CommonResult<Set<Long>> listRoleMenus(Long roleId) {
|
||||||
return success(permissionService.listRoleMenuIds(roleId));
|
return success(permissionService.getRoleMenuIds(roleId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/assign-role-menu")
|
@PostMapping("/assign-role-menu")
|
||||||
|
|
|
@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户套餐 Base VO,提供给添加、修改、详细的子 VO 使用
|
* 租户套餐 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||||
|
@ -26,6 +26,6 @@ public class TenantPackageBaseVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "关联的菜单编号", required = true)
|
@ApiModelProperty(value = "关联的菜单编号", required = true)
|
||||||
@NotNull(message = "关联的菜单编号不能为空")
|
@NotNull(message = "关联的菜单编号不能为空")
|
||||||
private List<Long> menuIds;
|
private Set<Long> menuIds;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
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.mybatis.core.type.JsonLongSetTypeHandler;
|
||||||
import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum;
|
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 cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
@ -21,7 +21,7 @@ import java.util.Set;
|
||||||
@TableName(value = "system_role", autoResultMap = true)
|
@TableName(value = "system_role", autoResultMap = true)
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class RoleDO extends BaseDO {
|
public class RoleDO extends TenantBaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色ID
|
* 角色ID
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
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.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
@ -14,7 +15,7 @@ import lombok.EqualsAndHashCode;
|
||||||
@TableName("system_role_menu")
|
@TableName("system_role_menu")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@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.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
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.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.RoleExportReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
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) {
|
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 调用,此时获取不到租户编号
|
@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.mapper.BaseMapperX;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,7 +45,14 @@ public interface TenantMapper extends BaseMapperX<TenantDO> {
|
||||||
}
|
}
|
||||||
|
|
||||||
default Integer selectCountByPackageId(Long packageId) {
|
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
|
@AllArgsConstructor
|
||||||
public enum RoleCodeEnum {
|
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} 的消费者
|
* 针对 {@link SmsSendMessage} 的消费者
|
||||||
*
|
*
|
||||||
* @author zzf
|
* @author zzf
|
||||||
* @date 2021/3/9 16:35
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@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.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
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.DeptCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
|
||||||
|
@ -73,9 +73,10 @@ public class DeptServiceImpl implements DeptService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@TenantIgnore // 初始化缓存,无需租户过滤
|
||||||
public synchronized void initLocalCache() {
|
public synchronized void initLocalCache() {
|
||||||
// 获取部门列表,如果有更新
|
// 获取部门列表,如果有更新
|
||||||
List<DeptDO> deptList = this.loadDeptIfUpdate(maxUpdateTime);
|
List<DeptDO> deptList = loadDeptIfUpdate(maxUpdateTime);
|
||||||
if (CollUtil.isEmpty(deptList)) {
|
if (CollUtil.isEmpty(deptList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -90,8 +91,7 @@ public class DeptServiceImpl implements DeptService {
|
||||||
// 设置缓存
|
// 设置缓存
|
||||||
deptCache = builder.build();
|
deptCache = builder.build();
|
||||||
parentDeptCache = parentBuilder.build();
|
parentDeptCache = parentBuilder.build();
|
||||||
assert deptList.size() > 0; // 断言,避免告警
|
maxUpdateTime = CollectionUtils.getMaxValue(deptList, DeptDO::getUpdateTime);
|
||||||
maxUpdateTime = deptList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initLocalCache][初始化 Dept 数量为 {}]", deptList.size());
|
log.info("[initLocalCache][初始化 Dept 数量为 {}]", deptList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ public class DeptServiceImpl implements DeptService {
|
||||||
* @param maxUpdateTime 当前部门的最大更新时间
|
* @param maxUpdateTime 当前部门的最大更新时间
|
||||||
* @return 部门列表
|
* @return 部门列表
|
||||||
*/
|
*/
|
||||||
private List<DeptDO> loadDeptIfUpdate(Date maxUpdateTime) {
|
protected List<DeptDO> loadDeptIfUpdate(Date maxUpdateTime) {
|
||||||
// 第一步,判断是否要更新。
|
// 第一步,判断是否要更新。
|
||||||
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||||
log.info("[loadMenuIfUpdate][首次加载全量部门]");
|
log.info("[loadMenuIfUpdate][首次加载全量部门]");
|
||||||
|
@ -118,7 +118,7 @@ public class DeptServiceImpl implements DeptService {
|
||||||
log.info("[loadMenuIfUpdate][增量加载全量部门]");
|
log.info("[loadMenuIfUpdate][增量加载全量部门]");
|
||||||
}
|
}
|
||||||
// 第二步,如果有更新,则从数据库加载所有部门
|
// 第二步,如果有更新,则从数据库加载所有部门
|
||||||
return deptMapper.selectListIgnoreTenant();
|
return deptMapper.selectList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.system.service.dict;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.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.DictDataCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
|
||||||
|
@ -99,8 +99,7 @@ public class DictDataServiceImpl implements DictDataService {
|
||||||
});
|
});
|
||||||
labelDictDataCache = labelDictDataBuilder.build();
|
labelDictDataCache = labelDictDataBuilder.build();
|
||||||
valueDictDataCache = valueDictDataBuilder.build();
|
valueDictDataCache = valueDictDataBuilder.build();
|
||||||
assert dataList.size() > 0; // 断言,避免告警
|
maxUpdateTime = CollectionUtils.getMaxValue(dataList, DictDataDO::getUpdateTime);
|
||||||
maxUpdateTime = dataList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
|
log.info("[initLocalCache][缓存字典数据,数量为:{}]", dataList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ public interface MenuService {
|
||||||
* @param menusStatuses 菜单状态数组
|
* @param menusStatuses 菜单状态数组
|
||||||
* @return 菜单列表
|
* @return 菜单列表
|
||||||
*/
|
*/
|
||||||
List<MenuDO> listMenusFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses);
|
List<MenuDO> getMenuListFromCache(Collection<Integer> menuTypes, Collection<Integer> menusStatuses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得指定编号的菜单数组,从缓存中
|
* 获得指定编号的菜单数组,从缓存中
|
||||||
|
@ -78,8 +78,8 @@ public interface MenuService {
|
||||||
* @param menusStatuses 菜单状态数组
|
* @param menusStatuses 菜单状态数组
|
||||||
* @return 菜单数组
|
* @return 菜单数组
|
||||||
*/
|
*/
|
||||||
List<MenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||||
Collection<Integer> menusStatuses);
|
Collection<Integer> menusStatuses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得权限对应的菜单数组
|
* 获得权限对应的菜单数组
|
||||||
|
|
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.service.permission;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
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.MenuCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuUpdateReqVO;
|
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.MenuIdEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
||||||
import cn.iocoder.yudao.module.system.mq.producer.permission.MenuProducer;
|
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.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
|
@ -95,8 +94,7 @@ public class MenuServiceImpl implements MenuService {
|
||||||
});
|
});
|
||||||
menuCache = menuCacheBuilder.build();
|
menuCache = menuCacheBuilder.build();
|
||||||
permissionMenuCache = permMenuCacheBuilder.build();
|
permissionMenuCache = permMenuCacheBuilder.build();
|
||||||
assert menuList.size() > 0; // 断言,避免告警
|
maxUpdateTime = CollectionUtils.getMaxValue(menuList, MenuDO::getUpdateTime);
|
||||||
maxUpdateTime = menuList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
|
log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +199,7 @@ public class MenuServiceImpl implements MenuService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)) {
|
if (CollectionUtils.isAnyEmpty(menuTypes, menusStatuses)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
@ -213,8 +211,8 @@ public class MenuServiceImpl implements MenuService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
public List<MenuDO> getMenuListFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
|
||||||
Collection<Integer> menusStatuses) {
|
Collection<Integer> menusStatuses) {
|
||||||
// 任一一个参数为空,则返回空
|
// 任一一个参数为空,则返回空
|
||||||
if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {
|
if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
|
@ -33,8 +33,8 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
|
||||||
* @param menusStatuses 菜单状态数组
|
* @param menusStatuses 菜单状态数组
|
||||||
* @return 菜单列表
|
* @return 菜单列表
|
||||||
*/
|
*/
|
||||||
List<MenuDO> getRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||||
Collection<Integer> menusStatuses);
|
Collection<Integer> menusStatuses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得用户拥有的角色编号集合
|
* 获得用户拥有的角色编号集合
|
||||||
|
@ -51,7 +51,7 @@ public interface PermissionService extends SecurityPermissionFrameworkService, D
|
||||||
* @param roleId 角色编号
|
* @param roleId 角色编号
|
||||||
* @return 菜单编号集合
|
* @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.CollUtil;
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
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.dept.DeptDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
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.dal.mysql.permission.UserRoleMapper;
|
||||||
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
|
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
|
||||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
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.ImmutableMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
@ -94,10 +95,10 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@TenantIgnore // 初始化缓存,无需租户过滤
|
||||||
public void initLocalCache() {
|
public void initLocalCache() {
|
||||||
Date now = new Date();
|
|
||||||
// 获取角色与菜单的关联列表,如果有更新
|
// 获取角色与菜单的关联列表,如果有更新
|
||||||
List<RoleMenuDO> roleMenuList = this.loadRoleMenuIfUpdate(maxUpdateTime);
|
List<RoleMenuDO> roleMenuList = loadRoleMenuIfUpdate(maxUpdateTime);
|
||||||
if (CollUtil.isEmpty(roleMenuList)) {
|
if (CollUtil.isEmpty(roleMenuList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -111,8 +112,7 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
});
|
});
|
||||||
roleMenuCache = roleMenuCacheBuilder.build();
|
roleMenuCache = roleMenuCacheBuilder.build();
|
||||||
menuRoleCache = menuRoleCacheBuilder.build();
|
menuRoleCache = menuRoleCacheBuilder.build();
|
||||||
assert roleMenuList.size() > 0; // 断言,避免告警
|
maxUpdateTime = CollectionUtils.getMaxValue(roleMenuList, RoleMenuDO::getUpdateTime);
|
||||||
maxUpdateTime = now;
|
|
||||||
log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
|
log.info("[initLocalCache][初始化角色与菜单的关联数量为 {}]", roleMenuList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
* @param maxUpdateTime 当前角色与菜单的关联的最大更新时间
|
* @param maxUpdateTime 当前角色与菜单的关联的最大更新时间
|
||||||
* @return 角色与菜单的关联列表
|
* @return 角色与菜单的关联列表
|
||||||
*/
|
*/
|
||||||
private List<RoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) {
|
protected List<RoleMenuDO> loadRoleMenuIfUpdate(Date maxUpdateTime) {
|
||||||
// 第一步,判断是否要更新。
|
// 第一步,判断是否要更新。
|
||||||
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
|
||||||
log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]");
|
log.info("[loadRoleMenuIfUpdate][首次加载全量角色与菜单的关联]");
|
||||||
|
@ -143,21 +143,22 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MenuDO> getRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
public List<MenuDO> getRoleMenuListFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||||
Collection<Integer> menusStatuses) {
|
Collection<Integer> menusStatuses) {
|
||||||
// 任一一个参数为空时,不返回任何菜单
|
// 任一一个参数为空时,不返回任何菜单
|
||||||
if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
|
if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
// 判断角色是否包含管理员
|
|
||||||
|
// 判断角色是否包含超级管理员。如果是超级管理员,获取到全部
|
||||||
List<RoleDO> roleList = roleService.getRolesFromCache(roleIds);
|
List<RoleDO> roleList = roleService.getRolesFromCache(roleIds);
|
||||||
boolean hasAdmin = roleService.hasAnyAdmin(roleList);
|
if (roleService.hasAnySuperAdmin(roleList)) {
|
||||||
// 获得角色拥有的菜单关联
|
return menuService.getMenuListFromCache(menuTypes, menusStatuses);
|
||||||
if (hasAdmin) { // 管理员,获取到全部
|
|
||||||
return menuService.listMenusFromCache(menuTypes, menusStatuses);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得角色拥有的菜单关联
|
||||||
List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);
|
List<Long> menuIds = MapUtils.getList(roleMenuCache, roleIds);
|
||||||
return menuService.listMenusFromCache(menuIds, menuTypes, menusStatuses);
|
return menuService.getMenuListFromCache(menuIds, menuTypes, menusStatuses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,10 +175,10 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> listRoleMenuIds(Long roleId) {
|
public Set<Long> getRoleMenuIds(Long roleId) {
|
||||||
// 如果是管理员的情况下,获取全部菜单编号
|
// 如果是管理员的情况下,获取全部菜单编号
|
||||||
RoleDO role = roleService.getRole(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);
|
return CollectionUtils.convertSet(menuService.getMenus(), MenuDO::getId);
|
||||||
}
|
}
|
||||||
// 如果是非管理员的情况下,获得拥有的菜单编号
|
// 如果是非管理员的情况下,获得拥有的菜单编号
|
||||||
|
@ -302,7 +303,7 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 判断是否是超管。如果是,当然符合条件
|
// 判断是否是超管。如果是,当然符合条件
|
||||||
if (roleService.hasAnyAdmin(roleIds)) {
|
if (roleService.hasAnySuperAdmin(roleIds)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +338,7 @@ public class PermissionServiceImpl implements PermissionService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 判断是否是超管。如果是,当然符合条件
|
// 判断是否是超管。如果是,当然符合条件
|
||||||
if (roleService.hasAnyAdmin(roleIds)) {
|
if (roleService.hasAnySuperAdmin(roleIds)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Set<String> userRoles = CollectionUtils.convertSet(roleService.getRolesFromCache(roleIds),
|
Set<String> userRoles = CollectionUtils.convertSet(roleService.getRolesFromCache(roleIds),
|
||||||
|
|
|
@ -90,12 +90,12 @@ public interface RoleService {
|
||||||
List<RoleDO> getRolesFromCache(Collection<Long> ids);
|
List<RoleDO> getRolesFromCache(Collection<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断角色数组中,是否有管理员
|
* 判断角色数组中,是否有超级管理员
|
||||||
*
|
*
|
||||||
* @param roleList 角色数组
|
* @param roleList 角色数组
|
||||||
* @return 是否有管理员
|
* @return 是否有管理员
|
||||||
*/
|
*/
|
||||||
boolean hasAnyAdmin(Collection<RoleDO> roleList);
|
boolean hasAnySuperAdmin(Collection<RoleDO> roleList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断角色编号数组中,是否有管理员
|
* 判断角色编号数组中,是否有管理员
|
||||||
|
@ -103,8 +103,8 @@ public interface RoleService {
|
||||||
* @param ids 角色编号数组
|
* @param ids 角色编号数组
|
||||||
* @return 是否有管理员
|
* @return 是否有管理员
|
||||||
*/
|
*/
|
||||||
default boolean hasAnyAdmin(Set<Long> ids) {
|
default boolean hasAnySuperAdmin(Set<Long> ids) {
|
||||||
return hasAnyAdmin(getRolesFromCache(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.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
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.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.RoleCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
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.enums.permission.RoleTypeEnum;
|
||||||
import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;
|
import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
@ -78,19 +77,17 @@ public class RoleServiceImpl implements RoleService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@TenantIgnore // 忽略自动多租户,全局初始化缓存
|
||||||
public void initLocalCache() {
|
public void initLocalCache() {
|
||||||
// 获取角色列表,如果有更新
|
// 获取角色列表,如果有更新
|
||||||
List<RoleDO> roleList = this.loadRoleIfUpdate(maxUpdateTime);
|
List<RoleDO> roleList = loadRoleIfUpdate(maxUpdateTime);
|
||||||
if (CollUtil.isEmpty(roleList)) {
|
if (CollUtil.isEmpty(roleList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入缓存
|
// 写入缓存
|
||||||
ImmutableMap.Builder<Long, RoleDO> builder = ImmutableMap.builder();
|
roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId);
|
||||||
roleList.forEach(sysRoleDO -> builder.put(sysRoleDO.getId(), sysRoleDO));
|
maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime);
|
||||||
roleCache = builder.build();
|
|
||||||
assert roleList.size() > 0; // 断言,避免告警
|
|
||||||
maxUpdateTime = roleList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initLocalCache][初始化 Role 数量为 {}]", roleList.size());
|
log.info("[initLocalCache][初始化 Role 数量为 {}]", roleList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,11 +213,11 @@ public class RoleServiceImpl implements RoleService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAnyAdmin(Collection<RoleDO> roleList) {
|
public boolean hasAnySuperAdmin(Collection<RoleDO> roleList) {
|
||||||
if (CollectionUtil.isEmpty(roleList)) {
|
if (CollectionUtil.isEmpty(roleList)) {
|
||||||
return false;
|
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
|
@Override
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package cn.iocoder.yudao.module.system.service.sms;
|
package cn.iocoder.yudao.module.system.service.sms;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
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.SmsChannelCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
|
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.dataobject.sms.SmsChannelDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
||||||
import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -19,13 +19,12 @@ import org.springframework.stereotype.Service;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
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_HAS_CHILDREN;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
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实现类
|
* 短信渠道Service实现类
|
||||||
|
@ -74,8 +73,7 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
||||||
propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
|
propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
|
||||||
|
|
||||||
// 写入缓存
|
// 写入缓存
|
||||||
assert smsChannels.size() > 0; // 断言,避免告警
|
maxUpdateTime = CollectionUtils.getMaxValue(smsChannels, SmsChannelDO::getUpdateTime);
|
||||||
maxUpdateTime = smsChannels.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initSmsClients][初始化 SmsChannel 数量为 {}]", smsChannels.size());
|
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.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ReUtil;
|
import cn.hutool.core.util.ReUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
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.SmsTemplateCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
|
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.SmsTemplatePageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateUpdateReqVO;
|
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.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.dataobject.sms.SmsTemplateDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
|
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.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.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -31,8 +30,8 @@ import javax.annotation.Resource;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
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.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信模板 Service 实现类
|
* 短信模板 Service 实现类
|
||||||
|
@ -89,11 +88,8 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入缓存
|
// 写入缓存
|
||||||
ImmutableMap.Builder<String, SmsTemplateDO> builder = ImmutableMap.builder();
|
smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode);
|
||||||
smsTemplateList.forEach(sysSmsTemplateDO -> builder.put(sysSmsTemplateDO.getCode(), sysSmsTemplateDO));
|
maxUpdateTime = CollectionUtils.getMaxValue(smsTemplateList, SmsTemplateDO::getUpdateTime);
|
||||||
smsTemplateCache = builder.build();
|
|
||||||
assert smsTemplateList.size() > 0; // 断言,避免告警
|
|
||||||
maxUpdateTime = smsTemplateList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
|
|
||||||
log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size());
|
log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
package cn.iocoder.yudao.module.system.service.tenant;
|
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.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.TenantPackageCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO;
|
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.controller.admin.tenant.vo.packages.TenantPackageUpdateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert;
|
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.dataobject.tenant.TenantPackageDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -45,12 +48,18 @@ public class TenantPackageServiceImpl implements TenantPackageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) {
|
public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
this.validateTenantPackageExists(updateReqVO.getId());
|
TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId());
|
||||||
// 更新
|
// 更新
|
||||||
TenantPackageDO updateObj = TenantPackageConvert.INSTANCE.convert(updateReqVO);
|
TenantPackageDO updateObj = TenantPackageConvert.INSTANCE.convert(updateReqVO);
|
||||||
tenantPackageMapper.updateById(updateObj);
|
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
|
@Override
|
||||||
|
@ -63,10 +72,12 @@ public class TenantPackageServiceImpl implements TenantPackageService {
|
||||||
tenantPackageMapper.deleteById(id);
|
tenantPackageMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateTenantPackageExists(Long id) {
|
private TenantPackageDO validateTenantPackageExists(Long id) {
|
||||||
if (tenantPackageMapper.selectById(id) == null) {
|
TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id);
|
||||||
|
if (tenantPackage == null) {
|
||||||
throw exception(TENANT_PACKAGE_NOT_EXISTS);
|
throw exception(TENANT_PACKAGE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
return tenantPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateTenantUsed(Long id) {
|
private void validateTenantUsed(Long id) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户 Service 接口
|
* 租户 Service 接口
|
||||||
|
@ -19,6 +20,11 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public interface TenantService extends TenantFrameworkService {
|
public interface TenantService extends TenantFrameworkService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化租户的本地缓存
|
||||||
|
*/
|
||||||
|
void initLocalCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建租户
|
* 创建租户
|
||||||
*
|
*
|
||||||
|
@ -34,6 +40,14 @@ public interface TenantService extends TenantFrameworkService {
|
||||||
*/
|
*/
|
||||||
void updateTenant(@Valid TenantUpdateReqVO updateReqVO);
|
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);
|
Integer getTenantCountByPackageId(Long packageId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得使用指定套餐的租户数组
|
||||||
|
*
|
||||||
|
* @param packageId 租户套餐编号
|
||||||
|
* @return 租户数组
|
||||||
|
*/
|
||||||
|
List<TenantDO> getTenantListByPackageId(Long packageId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package cn.iocoder.yudao.module.system.service.tenant;
|
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.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
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.TenantPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO;
|
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.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.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.dal.mysql.tenant.TenantMapper;
|
||||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
|
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.PermissionService;
|
||||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
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.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
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 org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
|
@ -37,8 +46,27 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@Validated
|
@Validated
|
||||||
|
@Slf4j
|
||||||
public class TenantServiceImpl implements TenantService {
|
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
|
@Resource
|
||||||
private TenantMapper tenantMapper;
|
private TenantMapper tenantMapper;
|
||||||
|
|
||||||
|
@ -51,15 +79,61 @@ public class TenantServiceImpl implements TenantService {
|
||||||
@Resource
|
@Resource
|
||||||
private PermissionService permissionService;
|
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
|
@Override
|
||||||
public List<Long> getTenantIds() {
|
public List<Long> getTenantIds() {
|
||||||
List<TenantDO> tenants = tenantMapper.selectList();
|
return new ArrayList<>(tenantCache.keySet());
|
||||||
return CollectionUtils.convertList(tenants, TenantDO::getId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validTenant(Long id) {
|
public void validTenant(Long id) {
|
||||||
TenantDO tenant = tenantMapper.selectById(id);
|
TenantDO tenant = tenantCache.get(id);
|
||||||
if (tenant == null) {
|
if (tenant == null) {
|
||||||
throw exception(TENANT_NOT_EXISTS);
|
throw exception(TENANT_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +149,7 @@ public class TenantServiceImpl implements TenantService {
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Long createTenant(TenantCreateReqVO createReqVO) {
|
public Long createTenant(TenantCreateReqVO createReqVO) {
|
||||||
// 校验套餐被禁用
|
// 校验套餐被禁用
|
||||||
tenantPackageService.validTenantPackage(createReqVO.getPackageId());
|
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
|
||||||
|
|
||||||
// 创建租户
|
// 创建租户
|
||||||
TenantDO tenant = TenantConvert.INSTANCE.convert(createReqVO);
|
TenantDO tenant = TenantConvert.INSTANCE.convert(createReqVO);
|
||||||
|
@ -83,13 +157,19 @@ public class TenantServiceImpl implements TenantService {
|
||||||
|
|
||||||
TenantUtils.execute(tenant.getId(), () -> {
|
TenantUtils.execute(tenant.getId(), () -> {
|
||||||
// 创建角色
|
// 创建角色
|
||||||
Long roleId = createRole();
|
Long roleId = createRole(tenantPackage);
|
||||||
// 创建用户,并分配角色
|
// 创建用户,并分配角色
|
||||||
Long userId = createUser(roleId, createReqVO);
|
Long userId = createUser(roleId, createReqVO);
|
||||||
// 修改租户的管理员
|
// 修改租户的管理员
|
||||||
tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
|
tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
|
||||||
});
|
});
|
||||||
// 返回
|
// 发送刷新消息
|
||||||
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||||
|
@Override
|
||||||
|
public void afterCommit() {
|
||||||
|
tenantProducer.sendTenantRefreshMessage();
|
||||||
|
}
|
||||||
|
});
|
||||||
return tenant.getId();
|
return tenant.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,36 +181,80 @@ public class TenantServiceImpl implements TenantService {
|
||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long createRole() {
|
private Long createRole(TenantPackageDO tenantPackage) {
|
||||||
|
// 创建角色
|
||||||
RoleCreateReqVO reqVO = new RoleCreateReqVO();
|
RoleCreateReqVO reqVO = new RoleCreateReqVO();
|
||||||
reqVO.setName(RoleCodeEnum.ADMIN.name()).setCode(RoleCodeEnum.ADMIN.getKey()).setSort(0);
|
reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())
|
||||||
return roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
|
.setSort(0).setRemark("系统自动生成");
|
||||||
|
Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
|
||||||
|
// 分配权限
|
||||||
|
permissionService.assignRoleMenu(roleId, tenantPackage.getMenuIds());
|
||||||
|
return roleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateTenant(TenantUpdateReqVO updateReqVO) {
|
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);
|
TenantDO updateObj = TenantConvert.INSTANCE.convert(updateReqVO);
|
||||||
tenantMapper.updateById(updateObj);
|
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
|
@Override
|
||||||
public void deleteTenant(Long id) {
|
public void deleteTenant(Long id) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
this.validateTenantExists(id);
|
validateTenantExists(id);
|
||||||
// 删除
|
// 删除
|
||||||
tenantMapper.deleteById(id);
|
tenantMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateTenantExists(Long id) {
|
private TenantDO validateTenantExists(Long id) {
|
||||||
if (tenantMapper.selectById(id) == null) {
|
TenantDO tenant = tenantMapper.selectById(id);
|
||||||
|
if (tenant == null) {
|
||||||
throw exception(TENANT_NOT_EXISTS);
|
throw exception(TENANT_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
return tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -163,4 +287,9 @@ public class TenantServiceImpl implements TenantService {
|
||||||
return tenantMapper.selectCountByPackageId(packageId);
|
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);
|
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
|
||||||
mockCacheMap.put(menuDO.getId(), menuDO);
|
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()));
|
Collections.singletonList(CommonStatusEnum.DISABLE.getStatus()));
|
||||||
assertEquals(menuDOS.size(), idMenuMap.size());
|
assertEquals(menuDOS.size(), idMenuMap.size());
|
||||||
menuDOS.forEach(m -> assertPojoEquals(idMenuMap.get(m.getId()), m));
|
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);
|
menuDO = createMenuDO(4L, MenuTypeEnum.MENU, "name", 0L, 2);
|
||||||
mockCacheMap.put(menuDO.getId(), menuDO);
|
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));
|
Collections.singletonList(MenuTypeEnum.MENU.getType()), Collections.singletonList(1));
|
||||||
assertEquals(menuDOS.size(), idMenuMap.size());
|
assertEquals(menuDOS.size(), idMenuMap.size());
|
||||||
menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu));
|
menuDOS.forEach(menu -> assertPojoEquals(idMenuMap.get(menu.getId()), menu));
|
||||||
|
|
|
@ -79,8 +79,8 @@ yudao:
|
||||||
- cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants
|
- cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants
|
||||||
tenant: # 多租户相关配置项
|
tenant: # 多租户相关配置项
|
||||||
enable: true
|
enable: true
|
||||||
ignore-urls: /admin-api/system/captcha/get-image, /admin-api/infra/file/get/*
|
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, tool_codegen_column, tool_codegen_table, tool_test_demo, tables, columns
|
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: # 短信验证码相关的配置项
|
sms-code: # 短信验证码相关的配置项
|
||||||
expire-times: 10m
|
expire-times: 10m
|
||||||
send-frequency: 1m
|
send-frequency: 1m
|
||||||
|
|
|
@ -200,6 +200,8 @@ export default {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.open = true;
|
this.open = true;
|
||||||
this.title = "添加租户套餐";
|
this.title = "添加租户套餐";
|
||||||
|
// 设置为非严格,继续使用半选中
|
||||||
|
this.menuCheckStrictly = false;
|
||||||
},
|
},
|
||||||
/** 修改按钮操作 */
|
/** 修改按钮操作 */
|
||||||
handleUpdate(row) {
|
handleUpdate(row) {
|
||||||
|
@ -222,12 +224,6 @@ export default {
|
||||||
/** 获得菜单 */
|
/** 获得菜单 */
|
||||||
getMenus() {
|
getMenus() {
|
||||||
listSimpleMenus().then(response => {
|
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 参数
|
// 处理 menuOptions 参数
|
||||||
this.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.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-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)
|
* 【新增】通过 `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/6b6d676a6baa2dad16ae9bf03d5002209064c8cc)
|
||||||
* 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 []()
|
* 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/2598c033a95d4b61d5f5ab3da5f1414f25c510d6)
|
||||||
|
|
||||||
### 🐞 Bug Fixes
|
### 🐞 Bug Fixes
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue