多租户:登录界面,根据 host 域名获取对应的租户编号

This commit is contained in:
YunaiV
2023-11-06 21:24:45 +08:00
parent ac0f9a020a
commit 0837f9adbc
20 changed files with 190 additions and 83 deletions

View File

@@ -104,6 +104,7 @@ public interface ErrorCodeConstants {
ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期");
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!");
ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在");
ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在");
// ========== 租户套餐 1-002-016-000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在");

View File

@@ -8,9 +8,9 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -37,8 +37,17 @@ public class TenantController {
@Operation(summary = "使用租户名,获得租户编号", description = "登录界面,根据用户的租户名,获得租户编号")
@Parameter(name = "name", description = "租户名", required = true, example = "1024")
public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) {
TenantDO tenantDO = tenantService.getTenantByName(name);
return success(tenantDO != null ? tenantDO.getId() : null);
TenantDO tenant = tenantService.getTenantByName(name);
return success(tenant != null ? tenant.getId() : null);
}
@GetMapping("/get-by-website")
@PermitAll
@Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息")
@Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn")
public CommonResult<TenantSimpleRespVO> getTenantByWebsite(@RequestParam("website") String website) {
TenantDO tenant = tenantService.getTenantByWebsite(website);
return success(TenantConvert.INSTANCE.convert03(tenant));
}
@PostMapping("/create")
@@ -94,5 +103,4 @@ public class TenantController {
ExcelUtils.write(response, "租户.xls", "数据", TenantExcelVO.class, datas);
}
}

View File

@@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import javax.validation.constraints.*;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
@@ -29,7 +29,7 @@ public class TenantBaseVO {
private Integer status;
@Schema(description = "绑定域名", example = "https://www.iocoder.cn")
private String domain;
private String website;
@Schema(description = "租户套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "租户套餐编号不能为空")

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 租户精简 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TenantSimpleRespVO extends TenantBaseVO {
@Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
}

View File

@@ -1,10 +1,7 @@
package cn.iocoder.yudao.module.system.convert.tenant;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.*;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserCreateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import org.mapstruct.Mapper;
@@ -28,6 +25,8 @@ public interface TenantConvert {
TenantRespVO convert(TenantDO bean);
TenantSimpleRespVO convert03(TenantDO bean);
List<TenantRespVO> convertList(List<TenantDO> list);
PageResult<TenantRespVO> convertPage(PageResult<TenantDO> page);

View File

@@ -59,10 +59,8 @@ public class TenantDO extends BaseDO {
private Integer status;
/**
* 绑定域名
*
* TODO 芋艿:目前是预留字段,未来会支持根据域名,自动查询到对应的租户。等等
*/
private String domain;
private String website;
/**
* 租户套餐编号
*

View File

@@ -42,6 +42,10 @@ public interface TenantMapper extends BaseMapperX<TenantDO> {
return selectOne(TenantDO::getName, name);
}
default TenantDO selectByWebsite(String website) {
return selectOne(TenantDO::getWebsite, website);
}
default Long selectCountByPackageId(Long packageId) {
return selectCount(TenantDO::getPackageId, packageId);
}

View File

@@ -78,11 +78,19 @@ public interface TenantService {
/**
* 获得名字对应的租户
*
* @param name 户名
* @param name 户名
* @return 租户
*/
TenantDO getTenantByName(String name);
/**
* 获得域名对应的租户
*
* @param website 域名
* @return 租户
*/
TenantDO getTenantByWebsite(String website);
/**
* 获得使用指定套餐的租户数量
*
@@ -128,4 +136,5 @@ public interface TenantService {
* @param id 租户编号
*/
void validTenant(Long id);
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.tenant;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -99,6 +100,8 @@ public class TenantServiceImpl implements TenantService {
public Long createTenant(TenantCreateReqVO createReqVO) {
// 校验租户名称是否重复
validTenantNameDuplicate(createReqVO.getName(), null);
// 校验租户域名是否重复
validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
// 校验套餐被禁用
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
@@ -143,6 +146,8 @@ public class TenantServiceImpl implements TenantService {
TenantDO tenant = validateUpdateTenant(updateReqVO.getId());
// 校验租户名称是否重复
validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId());
// 校验租户域名是否重复
validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId());
// 校验套餐被禁用
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
@@ -169,6 +174,23 @@ public class TenantServiceImpl implements TenantService {
}
}
private void validTenantWebsiteDuplicate(String website, Long id) {
if (StrUtil.isEmpty(website)) {
return;
}
TenantDO tenant = tenantMapper.selectByWebsite(website);
if (tenant == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同名字的租户
if (id == null) {
throw exception(TENANT_WEBSITE_DUPLICATE, website);
}
if (!tenant.getId().equals(id)) {
throw exception(TENANT_WEBSITE_DUPLICATE, website);
}
}
@Override
@DSTransactional
public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
@@ -234,6 +256,11 @@ public class TenantServiceImpl implements TenantService {
return tenantMapper.selectByName(name);
}
@Override
public TenantDO getTenantByWebsite(String website) {
return tenantMapper.selectByWebsite(website);
}
@Override
public Long getTenantCountByPackageId(Long packageId) {
return tenantMapper.selectCountByPackageId(packageId);

View File

@@ -151,6 +151,7 @@ yudao:
enable: true
ignore-urls:
- /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
- /admin-api/system/tenant/get-by-website # 基于域名获取租户,不许带租户编号
- /admin-api/system/captcha/get-image # 获取图片验证码,和租户无关
- /admin-api/system/captcha/get # 获取图片验证码,和租户无关
- /admin-api/system/captcha/check # 校验图片验证码,和租户无关

View File

@@ -157,7 +157,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
o.setContactMobile("15601691300");
o.setPackageId(100L);
o.setStatus(randomCommonStatus());
o.setDomain("https://www.iocoder.cn");
o.setWebsite("https://www.iocoder.cn");
o.setUsername("yunai");
o.setPassword("yuanma");
});
@@ -185,7 +185,7 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
TenantUpdateReqVO reqVO = randomPojo(TenantUpdateReqVO.class, o -> {
o.setId(dbTenant.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
o.setDomain(randomString());
o.setWebsite(randomString());
});
// mock 套餐

View File

@@ -411,7 +411,7 @@ CREATE TABLE IF NOT EXISTS "system_tenant" (
"contact_name" varchar(255) NOT NULL,
"contact_mobile" varchar(255),
"status" tinyint NOT NULL,
"domain" varchar(63) DEFAULT '',
"website" varchar(63) DEFAULT '',
"package_id" bigint NOT NULL,
"expire_time" timestamp NOT NULL,
"account_count" int NOT NULL,