Commit e4be51b14adfeba6d31424a30976d8729d441b9e

Authored by YunaiV
1 parent 4d539447

1. 新建租户、修改租户、修改租户套餐时,自动修改角色的权限

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