Commit 81d89ba3507e9aa1f5252614bd8cd4839a6bf848

Authored by YunaiV
1 parent 66ebb71b

增加租户、租户套餐的单元测试

yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
@@ -134,7 +134,7 @@ public class YudaoMQAutoConfiguration { @@ -134,7 +134,7 @@ public class YudaoMQAutoConfiguration {
134 String version = MapUtil.getStr(info, "redis_version"); 134 String version = MapUtil.getStr(info, "redis_version");
135 // 校验最低版本必须大于等于 5.0.0 135 // 校验最低版本必须大于等于 5.0.0
136 int majorVersion = Integer.parseInt(StrUtil.subBefore(version, '.', false)); 136 int majorVersion = Integer.parseInt(StrUtil.subBefore(version, '.', false));
137 - if (majorVersion < 7) { 137 + if (majorVersion < 5) {
138 throw new IllegalStateException(StrUtil.format("您当前的 Redis 版本为 {},小于最低要求的 5.0.0 版本!" + 138 throw new IllegalStateException(StrUtil.format("您当前的 Redis 版本为 {},小于最低要求的 5.0.0 版本!" +
139 "请参考 {} 文档进行安装。", version, DocumentEnum.REDIS_INSTALL.getUrl())); 139 "请参考 {} 文档进行安装。", version, DocumentEnum.REDIS_INSTALL.getUrl()));
140 } 140 }
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java
1 package cn.iocoder.yudao.module.system.controller.admin.tenant; 1 package cn.iocoder.yudao.module.system.controller.admin.tenant;
2 2
3 -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;  
4 -import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;  
5 -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;  
6 -import cn.iocoder.yudao.module.system.service.tenant.TenantService;  
7 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 5 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 6 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  7 +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
  8 +import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
  10 +import cn.iocoder.yudao.module.system.service.tenant.TenantService;
11 import io.swagger.annotations.Api; 11 import io.swagger.annotations.Api;
12 import io.swagger.annotations.ApiImplicitParam; 12 import io.swagger.annotations.ApiImplicitParam;
13 import io.swagger.annotations.ApiOperation; 13 import io.swagger.annotations.ApiOperation;
@@ -18,7 +18,6 @@ import javax.annotation.Resource; @@ -18,7 +18,6 @@ import javax.annotation.Resource;
18 import javax.servlet.http.HttpServletResponse; 18 import javax.servlet.http.HttpServletResponse;
19 import javax.validation.Valid; 19 import javax.validation.Valid;
20 import java.io.IOException; 20 import java.io.IOException;
21 -import java.util.Collection;  
22 import java.util.List; 21 import java.util.List;
23 22
24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -73,15 +72,6 @@ public class TenantController { @@ -73,15 +72,6 @@ public class TenantController {
73 return success(TenantConvert.INSTANCE.convert(tenant)); 72 return success(TenantConvert.INSTANCE.convert(tenant));
74 } 73 }
75 74
76 - @GetMapping("/list")  
77 - @ApiOperation("获得租户列表")  
78 - @ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)  
79 - @PreAuthorize("@ss.hasPermission('system:tenant:query')")  
80 - public CommonResult<List<TenantRespVO>> getTenantList(@RequestParam("ids") Collection<Long> ids) {  
81 - List<TenantDO> list = tenantService.getTenantList(ids);  
82 - return success(TenantConvert.INSTANCE.convertList(list));  
83 - }  
84 -  
85 @GetMapping("/page") 75 @GetMapping("/page")
86 @ApiOperation("获得租户分页") 76 @ApiOperation("获得租户分页")
87 @PreAuthorize("@ss.hasPermission('system:tenant:query')") 77 @PreAuthorize("@ss.hasPermission('system:tenant:query')")
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java
@@ -21,7 +21,7 @@ public interface TenantPackageMapper extends BaseMapperX&lt;TenantPackageDO&gt; { @@ -21,7 +21,7 @@ public interface TenantPackageMapper extends BaseMapperX&lt;TenantPackageDO&gt; {
21 return selectPage(reqVO, new LambdaQueryWrapperX<TenantPackageDO>() 21 return selectPage(reqVO, new LambdaQueryWrapperX<TenantPackageDO>()
22 .likeIfPresent(TenantPackageDO::getName, reqVO.getName()) 22 .likeIfPresent(TenantPackageDO::getName, reqVO.getName())
23 .eqIfPresent(TenantPackageDO::getStatus, reqVO.getStatus()) 23 .eqIfPresent(TenantPackageDO::getStatus, reqVO.getStatus())
24 - .eqIfPresent(TenantPackageDO::getRemark, reqVO.getRemark()) 24 + .likeIfPresent(TenantPackageDO::getRemark, reqVO.getRemark())
25 .betweenIfPresent(TenantPackageDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) 25 .betweenIfPresent(TenantPackageDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
26 .orderByDesc(TenantPackageDO::getId)); 26 .orderByDesc(TenantPackageDO::getId));
27 } 27 }
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
@@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.system.service.logger; @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.system.service.logger;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.string.StrUtils;
  7 +import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;
5 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogExportReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogExportReqVO;
6 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
7 import cn.iocoder.yudao.module.system.convert.logger.OperateLogConvert; 10 import cn.iocoder.yudao.module.system.convert.logger.OperateLogConvert;
@@ -9,9 +12,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; @@ -9,9 +12,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
9 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
10 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; 13 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper;
11 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 14 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
12 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
13 -import cn.iocoder.yudao.framework.common.util.string.StrUtils;  
14 -import cn.iocoder.yudao.framework.operatelog.core.dto.OperateLogCreateReqDTO;  
15 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
16 import org.springframework.scheduling.annotation.Async; 16 import org.springframework.scheduling.annotation.Async;
17 import org.springframework.scheduling.annotation.AsyncResult; 17 import org.springframework.scheduling.annotation.AsyncResult;
@@ -24,9 +24,9 @@ import java.util.Collections; @@ -24,9 +24,9 @@ import java.util.Collections;
24 import java.util.List; 24 import java.util.List;
25 import java.util.concurrent.Future; 25 import java.util.concurrent.Future;
26 26
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
27 import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH; 28 import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH;
28 import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.RESULT_MAX_LENGTH; 29 import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.RESULT_MAX_LENGTH;
29 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;  
30 30
31 @Service 31 @Service
32 @Validated 32 @Validated
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java
@@ -12,7 +12,6 @@ import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; @@ -12,7 +12,6 @@ import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
12 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; 12 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
13 13
14 import javax.validation.Valid; 14 import javax.validation.Valid;
15 -import java.util.Collection;  
16 import java.util.List; 15 import java.util.List;
17 import java.util.Set; 16 import java.util.Set;
18 17
@@ -67,14 +66,6 @@ public interface TenantService extends TenantFrameworkService { @@ -67,14 +66,6 @@ public interface TenantService extends TenantFrameworkService {
67 TenantDO getTenant(Long id); 66 TenantDO getTenant(Long id);
68 67
69 /** 68 /**
70 - * 获得租户列表  
71 - *  
72 - * @param ids 编号  
73 - * @return 租户列表  
74 - */  
75 - List<TenantDO> getTenantList(Collection<Long> ids);  
76 -  
77 - /**  
78 * 获得租户分页 69 * 获得租户分页
79 * 70 *
80 * @param pageReqVO 分页查询 71 * @param pageReqVO 分页查询
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
@@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService; @@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.system.service.permission.RoleService;
30 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; 30 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
31 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; 31 import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
32 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 32 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  33 +import lombok.Getter;
33 import lombok.extern.slf4j.Slf4j; 34 import lombok.extern.slf4j.Slf4j;
34 import org.springframework.context.annotation.Lazy; 35 import org.springframework.context.annotation.Lazy;
35 import org.springframework.scheduling.annotation.Scheduled; 36 import org.springframework.scheduling.annotation.Scheduled;
@@ -44,7 +45,10 @@ import javax.annotation.Resource; @@ -44,7 +45,10 @@ import javax.annotation.Resource;
44 import java.util.*; 45 import java.util.*;
45 46
46 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 47 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  48 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertImmutableMap;
  49 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
47 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 50 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  51 +import static java.util.Collections.singleton;
48 52
49 /** 53 /**
50 * 租户 Service 实现类 54 * 租户 Service 实现类
@@ -68,10 +72,12 @@ public class TenantServiceImpl implements TenantService { @@ -68,10 +72,12 @@ public class TenantServiceImpl implements TenantService {
68 * 72 *
69 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 73 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
70 */ 74 */
  75 + @Getter
71 private volatile Map<Long, TenantDO> tenantCache; 76 private volatile Map<Long, TenantDO> tenantCache;
72 /** 77 /**
73 * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新 78 * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
74 */ 79 */
  80 + @Getter
75 private volatile Date maxUpdateTime; 81 private volatile Date maxUpdateTime;
76 82
77 @Resource 83 @Resource
@@ -108,8 +114,8 @@ public class TenantServiceImpl implements TenantService { @@ -108,8 +114,8 @@ public class TenantServiceImpl implements TenantService {
108 } 114 }
109 115
110 // 写入缓存 116 // 写入缓存
111 - tenantCache = CollectionUtils.convertImmutableMap(tenantList, TenantDO::getId);  
112 - maxUpdateTime = CollectionUtils.getMaxValue(tenantList, TenantDO::getUpdateTime); 117 + tenantCache = convertImmutableMap(tenantList, TenantDO::getId);
  118 + maxUpdateTime = getMaxValue(tenantList, TenantDO::getUpdateTime);
113 log.info("[initLocalCache][初始化 Tenant 数量为 {}]", tenantList.size()); 119 log.info("[initLocalCache][初始化 Tenant 数量为 {}]", tenantList.size());
114 } 120 }
115 121
@@ -190,7 +196,7 @@ public class TenantServiceImpl implements TenantService { @@ -190,7 +196,7 @@ public class TenantServiceImpl implements TenantService {
190 // 创建用户 196 // 创建用户
191 Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO)); 197 Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO));
192 // 分配角色 198 // 分配角色
193 - permissionService.assignUserRole(userId, Collections.singleton(roleId)); 199 + permissionService.assignUserRole(userId, singleton(roleId));
194 return userId; 200 return userId;
195 } 201 }
196 202
@@ -280,11 +286,6 @@ public class TenantServiceImpl implements TenantService { @@ -280,11 +286,6 @@ public class TenantServiceImpl implements TenantService {
280 } 286 }
281 287
282 @Override 288 @Override
283 - public List<TenantDO> getTenantList(Collection<Long> ids) {  
284 - return tenantMapper.selectBatchIds(ids);  
285 - }  
286 -  
287 - @Override  
288 public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) { 289 public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
289 return tenantMapper.selectPage(pageReqVO); 290 return tenantMapper.selectPage(pageReqVO);
290 } 291 }
yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java
1 package cn.iocoder.yudao.module.system.service.tenant; 1 package cn.iocoder.yudao.module.system.service.tenant;
2 2
  3 +import cn.hutool.core.util.ReflectUtil;
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.tenant.config.TenantProperties; 6 import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
  7 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
6 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO;
7 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; 10 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO; 11 import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO;
  12 +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
  13 +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; 14 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
  15 +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; 16 import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
  17 +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
  18 +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
12 import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer; 19 import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer;
13 import cn.iocoder.yudao.module.system.service.permission.MenuService; 20 import cn.iocoder.yudao.module.system.service.permission.MenuService;
14 import cn.iocoder.yudao.module.system.service.permission.PermissionService; 21 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
15 import cn.iocoder.yudao.module.system.service.permission.RoleService; 22 import cn.iocoder.yudao.module.system.service.permission.RoleService;
  23 +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
  24 +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
16 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 25 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
17 import cn.iocoder.yudao.module.system.test.BaseDbUnitTest; 26 import cn.iocoder.yudao.module.system.test.BaseDbUnitTest;
  27 +import org.junit.jupiter.api.BeforeEach;
18 import org.junit.jupiter.api.Test; 28 import org.junit.jupiter.api.Test;
19 import org.springframework.boot.test.mock.mockito.MockBean; 29 import org.springframework.boot.test.mock.mockito.MockBean;
20 import org.springframework.context.annotation.Import; 30 import org.springframework.context.annotation.Import;
21 31
22 import javax.annotation.Resource; 32 import javax.annotation.Resource;
  33 +import java.time.Duration;
  34 +import java.util.Arrays;
  35 +import java.util.Collections;
23 import java.util.List; 36 import java.util.List;
  37 +import java.util.Map;
24 38
  39 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  40 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
25 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; 41 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
26 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 42 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  43 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
27 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 44 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
28 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 45 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
29 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 46 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
30 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.TENANT_NOT_EXISTS; 47 +import static cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO.PACKAGE_ID_SYSTEM;
  48 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  49 +import static java.util.Arrays.asList;
  50 +import static java.util.Collections.singleton;
31 import static org.junit.jupiter.api.Assertions.*; 51 import static org.junit.jupiter.api.Assertions.*;
  52 +import static org.mockito.ArgumentMatchers.*;
  53 +import static org.mockito.Mockito.*;
32 54
33 /** 55 /**
34 * {@link TenantServiceImpl} 的单元测试类 56 * {@link TenantServiceImpl} 的单元测试类
@@ -59,10 +81,118 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -59,10 +81,118 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
59 @MockBean 81 @MockBean
60 private TenantProducer tenantProducer; 82 private TenantProducer tenantProducer;
61 83
  84 + @BeforeEach
  85 + public void setUp() {
  86 + // 清理缓存
  87 + ReflectUtil.setFieldValue(tenantService, "tenantCache", Collections.emptyMap());
  88 + ReflectUtil.setFieldValue(tenantService, "maxUpdateTime", null);
  89 + // 清理租户上下文
  90 + TenantContextHolder.clear();
  91 + }
  92 +
  93 + @Test
  94 + public void testInitLocalCache() {
  95 + // mock 数据
  96 + TenantDO tenantDO1 = randomPojo(TenantDO.class);
  97 + tenantMapper.insert(tenantDO1);
  98 + TenantDO tenantDO2 = randomPojo(TenantDO.class);
  99 + tenantMapper.insert(tenantDO2);
  100 +
  101 + // 调用
  102 + tenantService.initLocalCache();
  103 + // 断言 tenantCache 缓存
  104 + Map<Long, TenantDO> tenantCache = tenantService.getTenantCache();
  105 + assertEquals(2, tenantCache.size());
  106 + assertPojoEquals(tenantDO1, tenantCache.get(tenantDO1.getId()));
  107 + assertPojoEquals(tenantDO2, tenantCache.get(tenantDO2.getId()));
  108 + // 断言 maxUpdateTime 缓存
  109 + assertEquals(max(tenantDO1.getUpdateTime(), tenantDO2.getUpdateTime()), tenantService.getMaxUpdateTime());
  110 + }
  111 +
  112 + @Test
  113 + public void testGetTenantIds() {
  114 + // mock 数据
  115 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L));
  116 + tenantMapper.insert(tenant);
  117 + tenantService.initLocalCache();
  118 +
  119 + // 调用,并断言业务异常
  120 + List<Long> result = tenantService.getTenantIds();
  121 + assertEquals(Collections.singletonList(1L), result);
  122 + }
  123 +
  124 + @Test
  125 + public void testValidTenant_notExists() {
  126 + assertServiceException(() -> tenantService.validTenant(randomLongId()), TENANT_NOT_EXISTS);
  127 + }
  128 +
  129 + @Test
  130 + public void testValidTenant_disable() {
  131 + // mock 数据
  132 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.DISABLE.getStatus()));
  133 + tenantMapper.insert(tenant);
  134 + tenantService.initLocalCache();
  135 +
  136 + // 调用,并断言业务异常
  137 + assertServiceException(() -> tenantService.validTenant(1L), TENANT_DISABLE, tenant.getName());
  138 + }
  139 +
  140 + @Test
  141 + public void testValidTenant_expired() {
  142 + // mock 数据
  143 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus())
  144 + .setExpireTime(buildTime(2020, 2, 2)));
  145 + tenantMapper.insert(tenant);
  146 + tenantService.initLocalCache();
  147 +
  148 + // 调用,并断言业务异常
  149 + assertServiceException(() -> tenantService.validTenant(1L), TENANT_EXPIRE, tenant.getName());
  150 + }
  151 +
  152 + @Test
  153 + public void testValidTenant_success() {
  154 + // mock 数据
  155 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus())
  156 + .setExpireTime(addTime(Duration.ofDays(1))));
  157 + tenantMapper.insert(tenant);
  158 + tenantService.initLocalCache();
  159 +
  160 + // 调用,并断言业务异常
  161 + tenantService.validTenant(1L);
  162 + }
  163 +
62 @Test 164 @Test
63 - public void testCreateTenant_success() { 165 + public void testCreateTenant() {
  166 + // mock 套餐 100L
  167 + TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, o -> o.setId(100L));
  168 + when(tenantPackageService.validTenantPackage(eq(100L))).thenReturn(tenantPackage);
  169 + // mock 角色 200L
  170 + when(roleService.createRole(argThat(role -> {
  171 + assertEquals(RoleCodeEnum.TENANT_ADMIN.getName(), role.getName());
  172 + assertEquals(RoleCodeEnum.TENANT_ADMIN.getCode(), role.getCode());
  173 + assertEquals(0, role.getSort());
  174 + assertEquals("系统自动生成", role.getRemark());
  175 + return true;
  176 + }), eq(RoleTypeEnum.SYSTEM.getType()))).thenReturn(200L);
  177 + // mock 用户 300L
  178 + when(userService.createUser(argThat(user -> {
  179 + assertEquals("yudao", user.getUsername());
  180 + assertEquals("yuanma", user.getPassword());
  181 + assertEquals("芋道", user.getNickname());
  182 + assertEquals("15601691300", user.getMobile());
  183 + return true;
  184 + }))).thenReturn(300L);
  185 +
64 // 准备参数 186 // 准备参数
65 - TenantCreateReqVO reqVO = randomPojo(TenantCreateReqVO.class, o -> o.setStatus(randomCommonStatus())); 187 + TenantCreateReqVO reqVO = randomPojo(TenantCreateReqVO.class, o -> {
  188 + o.setContactName("芋道");
  189 + o.setContactMobile("15601691300");
  190 + o.setPackageId(100L);
  191 + o.setStatus(randomCommonStatus());
  192 + o.setDomain("https://www.iocoder.cn");
  193 + o.setUsername("yudao");
  194 + o.setPassword("yuanma");
  195 + });
66 196
67 // 调用 197 // 调用
68 Long tenantId = tenantService.createTenant(reqVO); 198 Long tenantId = tenantService.createTenant(reqVO);
@@ -71,6 +201,13 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -71,6 +201,13 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
71 // 校验记录的属性是否正确 201 // 校验记录的属性是否正确
72 TenantDO tenant = tenantMapper.selectById(tenantId); 202 TenantDO tenant = tenantMapper.selectById(tenantId);
73 assertPojoEquals(reqVO, tenant); 203 assertPojoEquals(reqVO, tenant);
  204 + assertEquals(300L, tenant.getContactUserId());
  205 + // verify 分配权限
  206 + verify(permissionService).assignRoleMenu(eq(200L), same(tenantPackage.getMenuIds()));
  207 + // verify 分配角色
  208 + verify(permissionService).assignUserRole(eq(300L), eq(singleton(200L)));
  209 + // verify 发送刷新消息
  210 + verify(tenantProducer).sendTenantRefreshMessage();
74 } 211 }
75 212
76 @Test 213 @Test
@@ -82,13 +219,32 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -82,13 +219,32 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
82 TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class, o -> { 219 TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class, o -> {
83 o.setId(dbTenant.getId()); // 设置更新的 ID 220 o.setId(dbTenant.getId()); // 设置更新的 ID
84 o.setStatus(randomCommonStatus()); 221 o.setStatus(randomCommonStatus());
  222 + o.setDomain(randomString());
85 }); 223 });
86 224
  225 + // mock 套餐
  226 + TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class,
  227 + o -> o.setMenuIds(asSet(200L, 201L)));
  228 + when(tenantPackageService.validTenantPackage(eq(reqVO.getPackageId()))).thenReturn(tenantPackage);
  229 + // mock 所有角色
  230 + RoleDO role100 = randomPojo(RoleDO.class, o -> o.setId(100L).setCode(RoleCodeEnum.TENANT_ADMIN.getCode()));
  231 + role100.setTenantId(dbTenant.getId());
  232 + RoleDO role101 = randomPojo(RoleDO.class, o -> o.setId(101L));
  233 + role101.setTenantId(dbTenant.getId());
  234 + when(roleService.getRoles(isNull())).thenReturn(asList(role100, role101));
  235 + // mock 每个角色的权限
  236 + when(permissionService.getRoleMenuIds(eq(101L))).thenReturn(asSet(201L, 202L));
  237 +
87 // 调用 238 // 调用
88 tenantService.updateTenant(reqVO); 239 tenantService.updateTenant(reqVO);
89 // 校验是否更新正确 240 // 校验是否更新正确
90 TenantDO tenant = tenantMapper.selectById(reqVO.getId()); // 获取最新的 241 TenantDO tenant = tenantMapper.selectById(reqVO.getId()); // 获取最新的
91 assertPojoEquals(reqVO, tenant); 242 assertPojoEquals(reqVO, tenant);
  243 + // verify 发送刷新消息
  244 + verify(tenantProducer).sendTenantRefreshMessage();
  245 + // verify 设置角色权限
  246 + verify(permissionService).assignRoleMenu(eq(100L), eq(asSet(200L, 201L)));
  247 + verify(permissionService).assignRoleMenu(eq(101L), eq(asSet(201L)));
92 } 248 }
93 249
94 @Test 250 @Test
@@ -101,6 +257,20 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -101,6 +257,20 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
101 } 257 }
102 258
103 @Test 259 @Test
  260 + public void testUpdateTenant_system() {
  261 + // mock 数据
  262 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM));
  263 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  264 + // 准备参数
  265 + TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class, o -> {
  266 + o.setId(dbTenant.getId()); // 设置更新的 ID
  267 + });
  268 +
  269 + // 调用,校验业务异常
  270 + assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_CAN_NOT_UPDATE_SYSTEM);
  271 + }
  272 +
  273 + @Test
104 public void testDeleteTenant_success() { 274 public void testDeleteTenant_success() {
105 // mock 数据 275 // mock 数据
106 TenantDO dbTenant = randomPojo(TenantDO.class, 276 TenantDO dbTenant = randomPojo(TenantDO.class,
@@ -125,6 +295,32 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -125,6 +295,32 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
125 } 295 }
126 296
127 @Test 297 @Test
  298 + public void testDeleteTenant_system() {
  299 + // mock 数据
  300 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM));
  301 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  302 + // 准备参数
  303 + Long id = dbTenant.getId();
  304 +
  305 + // 调用, 并断言异常
  306 + assertServiceException(() -> tenantService.deleteTenant(id), TENANT_CAN_NOT_UPDATE_SYSTEM);
  307 + }
  308 +
  309 + @Test
  310 + public void testGetTenant() {
  311 + // mock 数据
  312 + TenantDO dbTenant = randomPojo(TenantDO.class);
  313 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  314 + // 准备参数
  315 + Long id = dbTenant.getId();
  316 +
  317 + // 调用
  318 + TenantDO result = tenantService.getTenant(id);
  319 + // 校验存在
  320 + assertPojoEquals(result, dbTenant);
  321 + }
  322 +
  323 + @Test
128 public void testGetTenantPage() { 324 public void testGetTenantPage() {
129 // mock 数据 325 // mock 数据
130 TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到 326 TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到
@@ -199,4 +395,129 @@ public class TenantServiceImplTest extends BaseDbUnitTest { @@ -199,4 +395,129 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
199 assertPojoEquals(dbTenant, list.get(0)); 395 assertPojoEquals(dbTenant, list.get(0));
200 } 396 }
201 397
  398 +
  399 + @Test
  400 + public void testGetTenantByName() {
  401 + // mock 数据
  402 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setName("芋道"));
  403 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  404 +
  405 + // 调用
  406 + TenantDO result = tenantService.getTenantByName("芋道");
  407 + // 校验存在
  408 + assertPojoEquals(result, dbTenant);
  409 + }
  410 +
  411 + @Test
  412 + public void testGetTenantListByPackageId() {
  413 + // mock 数据
  414 + TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L));
  415 + tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据
  416 + TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L));
  417 + tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据
  418 +
  419 + // 调用
  420 + List<TenantDO> result = tenantService.getTenantListByPackageId(1L);
  421 + assertEquals(1, result.size());
  422 + assertPojoEquals(dbTenant1, result.get(0));
  423 + }
  424 +
  425 + @Test
  426 + public void testGetTenantCountByPackageId() {
  427 + // mock 数据
  428 + TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L));
  429 + tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据
  430 + TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L));
  431 + tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据
  432 +
  433 + // 调用
  434 + Integer count = tenantService.getTenantCountByPackageId(1L);
  435 + assertEquals(1, count);
  436 + }
  437 +
  438 + @Test
  439 + public void testHandleTenantInfo_disable() {
  440 + // 准备参数
  441 + TenantInfoHandler handler = mock(TenantInfoHandler.class);
  442 + // mock 禁用
  443 + when(tenantProperties.getEnable()).thenReturn(false);
  444 +
  445 + // 调用
  446 + tenantService.handleTenantInfo(handler);
  447 + // 断言
  448 + verify(handler, never()).handle(any());
  449 + }
  450 +
  451 + @Test
  452 + public void testHandleTenantInfo_success() {
  453 + // 准备参数
  454 + TenantInfoHandler handler = mock(TenantInfoHandler.class);
  455 + // mock 未禁用
  456 + when(tenantProperties.getEnable()).thenReturn(true);
  457 + // mock 租户
  458 + TenantDO dbTenant = randomPojo(TenantDO.class);
  459 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  460 + TenantContextHolder.setTenantId(dbTenant.getId());
  461 +
  462 + // 调用
  463 + tenantService.handleTenantInfo(handler);
  464 + // 断言
  465 + verify(handler).handle(argThat(argument -> {
  466 + assertPojoEquals(dbTenant, argument);
  467 + return true;
  468 + }));
  469 + }
  470 +
  471 + @Test
  472 + public void testHandleTenantMenu_disable() {
  473 + // 准备参数
  474 + TenantMenuHandler handler = mock(TenantMenuHandler.class);
  475 + // mock 禁用
  476 + when(tenantProperties.getEnable()).thenReturn(false);
  477 +
  478 + // 调用
  479 + tenantService.handleTenantMenu(handler);
  480 + // 断言
  481 + verify(handler, never()).handle(any());
  482 + }
  483 +
  484 + @Test // 系统租户的情况
  485 + public void testHandleTenantMenu_system() {
  486 + // 准备参数
  487 + TenantMenuHandler handler = mock(TenantMenuHandler.class);
  488 + // mock 未禁用
  489 + when(tenantProperties.getEnable()).thenReturn(true);
  490 + // mock 租户
  491 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM));
  492 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  493 + TenantContextHolder.setTenantId(dbTenant.getId());
  494 + // mock 菜单
  495 + when(menuService.getMenus()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)),
  496 + randomPojo(MenuDO.class, o -> o.setId(101L))));
  497 +
  498 + // 调用
  499 + tenantService.handleTenantMenu(handler);
  500 + // 断言
  501 + verify(handler).handle(asSet(100L, 101L));
  502 + }
  503 +
  504 + @Test // 普通租户的情况
  505 + public void testHandleTenantMenu_normal() {
  506 + // 准备参数
  507 + TenantMenuHandler handler = mock(TenantMenuHandler.class);
  508 + // mock 未禁用
  509 + when(tenantProperties.getEnable()).thenReturn(true);
  510 + // mock 租户
  511 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(200L));
  512 + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
  513 + TenantContextHolder.setTenantId(dbTenant.getId());
  514 + // mock 菜单
  515 + when(tenantPackageService.getTenantPackage(eq(200L))).thenReturn(randomPojo(TenantPackageDO.class,
  516 + o -> o.setMenuIds(asSet(100L, 101L))));
  517 +
  518 + // 调用
  519 + tenantService.handleTenantMenu(handler);
  520 + // 断言
  521 + verify(handler).handle(asSet(100L, 101L));
  522 + }
202 } 523 }
yudao-module-system/yudao-module-system-impl/src/test/resources/sql/create_tables.sql
@@ -394,7 +394,7 @@ CREATE TABLE IF NOT EXISTS &quot;system_social_user&quot; ( @@ -394,7 +394,7 @@ CREATE TABLE IF NOT EXISTS &quot;system_social_user&quot; (
394 CREATE TABLE IF NOT EXISTS "system_tenant" ( 394 CREATE TABLE IF NOT EXISTS "system_tenant" (
395 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 395 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
396 "name" varchar(63) NOT NULL, 396 "name" varchar(63) NOT NULL,
397 - "contact_user_id" bigint NOT NULL, 397 + "contact_user_id" bigint NOT NULL DEFAULT '0',
398 "contact_name" varchar(255) NOT NULL, 398 "contact_name" varchar(255) NOT NULL,
399 "contact_mobile" varchar(255), 399 "contact_mobile" varchar(255),
400 "status" tinyint NOT NULL, 400 "status" tinyint NOT NULL,
更新日志.md
@@ -32,7 +32,7 @@ TODO @@ -32,7 +32,7 @@ TODO
32 * 【新增】新增 `@TenantIgnore` 注解,标记指定方法,忽略多租户的自动过滤,适合实现跨租户的逻辑 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/4d53944771c66b563da1e3d68d3ba43405af8a06) 32 * 【新增】新增 `@TenantIgnore` 注解,标记指定方法,忽略多租户的自动过滤,适合实现跨租户的逻辑 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/4d53944771c66b563da1e3d68d3ba43405af8a06)
33 * 【新增】租户套餐的管理,可配置每个租户的可使用的功能权限 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/6b6d676a6baa2dad16ae9bf03d5002209064c8cc) 33 * 【新增】租户套餐的管理,可配置每个租户的可使用的功能权限 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/6b6d676a6baa2dad16ae9bf03d5002209064c8cc)
34 * 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/2598c033a95d4b61d5f5ab3da5f1414f25c510d6) 34 * 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/2598c033a95d4b61d5f5ab3da5f1414f25c510d6)
35 -* 【优化】Redis 最低版本 5.0.0 检测,解决搭建环境过程中无法理解 XREADGROUP 指令的报错 [commit]() 35 +* 【优化】Redis 最低版本 5.0.0 检测,解决搭建环境过程中无法理解 XREADGROUP 指令的报错 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/c64bb81caeee2721e0c373eee014e3977d88cb37)
36 36
37 ### 🐞 Bug Fixes 37 ### 🐞 Bug Fixes
38 38