租户管理
一、核心功能
租户管理是 JNPF 框架提供的多租户支持系统,允许在同一应用实例中运行多个独立的租户,每个租户拥有独立的数据和配置。
1.1 核心价值
- 多租户架构:支持同一应用实例运行多个租户
- 数据隔离:支持列级隔离和数据库级隔离两种模式
- 租户配置:支持租户级别的配置管理
- 租户切换:支持运行时动态切换租户
- 可扩展性:支持自定义租户解析和租户存储
二、多租户模式
2.1 列级隔离(COLUMN)
所有租户共享同一个数据库,通过 f_tenant_id 列区分不同租户的数据:
sql
-- 查询指定租户的数据
SELECT * FROM user WHERE f_tenant_id = 'tenant-001';
适用场景:
- 租户数量较多(数百到数千)
- 数据量适中
- 数据库资源有限
2.2 数据库级隔离(SCHEMA)
每个租户拥有独立的数据库或数据库架构:
sql
-- 使用指定租户的数据库
USE tenant_001;
SELECT * FROM user;
适用场景:
- 租户数量较少(数十个以内)
- 数据量较大
- 需要更高的数据隔离级别
2.3 配置模式
在 Tenant.json 中配置多租户模式:
json
{
"Tenant": {
"MultiTenancy": true,
"MultiTenancyType": "COLUMN",
"MultiTenancyDBInterFace": "https://tenant.xxx.cn/api/Tenant/DbName/",
"MultiSystem": true
}
}
三、实现原理
3.1 租户解析
框架通过多种方式解析当前租户:
| 方式 | 说明 | 优先级 |
|---|---|---|
| 请求头 | 通过 jnpf-tenant-id 请求头传递租户 ID |
高 |
| URL 参数 | 通过 tenantId URL 参数传递租户 ID |
中 |
| Cookie | 通过 Cookie 传递租户 ID | 低 |
| 默认租户 | 使用配置的默认租户 ID | 默认 |
3.2 租户过滤器
框架自动为查询添加租户过滤器:
csharp
// SqlSugar 查询过滤器
db.QueryFilter.Add(new TableFilterItem<BaseUserEntity>(it => it.TenantId == currentTenantId));
四、使用示例
4.1 租户配置
在 Tenant.json 中配置多租户:
json
{
"Tenant": {
"MultiTenancy": true,
"MultiTenancyType": "COLUMN",
"MultiTenancyDBInterFace": "https://tenant.xxx.cn/api/Tenant/DbName/",
"MultiSystem": true
}
}
4.2 获取当前租户
csharp
public class UserService
{
public void Process()
{
var currentTenantId = App.GetTenantId();
// 使用当前租户ID查询数据
}
}
4.3 切换租户
csharp
public class TenantService
{
public void SwitchTenant(string tenantId)
{
App.SetTenantId(tenantId);
// 后续操作将使用新的租户ID
}
}
4.4 租户数据查询
框架自动为查询添加租户过滤器:
csharp
public class UserService
{
private readonly ISqlSugarClient _db;
public UserService(ISqlSugarClient db)
{
_db = db;
}
public async Task<List<UserInfo>> GetUserList()
{
// 框架自动添加租户过滤器
return await _db.Queryable<UserInfo>()
.Where(u => u.DeleteMark != 1)
.ToListAsync();
}
}
4.5 跨租户查询
使用 IgnoreQueryFilter 忽略租户过滤器:
csharp
public class AdminService
{
private readonly ISqlSugarClient _db;
public AdminService(ISqlSugarClient db)
{
_db = db;
}
public async Task<List<UserInfo>> GetAllUsers()
{
// 忽略租户过滤器,查询所有租户的数据
return await _db.Queryable<UserInfo>()
.IgnoreQueryFilter()
.Where(u => u.DeleteMark != 1)
.ToListAsync();
}
}
五、配置选项
5.1 租户配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
MultiTenancy |
false |
是否启用多租户 |
MultiTenancyType |
COLUMN |
多租户类型(COLUMN/SCHEMA) |
MultiTenancyDBInterFace |
null |
租户数据库接口地址 |
MultiSystem |
false |
是否启用多系统 |
DefaultTenantId |
null |
默认租户 ID |
5.2 租户数据库接口
当使用 SCHEMA 模式时,框架通过租户数据库接口获取租户的数据库连接信息:
json
{
"Tenant": {
"MultiTenancyDBInterFace": "https://tenant.xxx.cn/api/Tenant/DbName/"
}
}
接口返回格式:
json
{
"code": 200,
"data": {
"dbName": "tenant_001",
"connectionString": "Server=localhost;Database=tenant_001;Trusted_Connection=True;"
}
}
六、高级特性
6.1 租户初始化
为新租户初始化数据:
csharp
public class TenantService
{
public async Task CreateTenant(TenantInput input)
{
// 创建租户记录
var tenant = new TenantEntity
{
Id = input.Id,
Name = input.Name,
CreateTime = DateTime.Now
};
await _db.Insertable(tenant).ExecuteCommandAsync();
// 初始化租户数据
await InitializeTenantData(input.Id);
}
private async Task InitializeTenantData(string tenantId)
{
// 设置当前租户
var originalTenantId = App.GetTenantId();
App.SetTenantId(tenantId);
try
{
// 初始化基础数据
await InitializeRoles(tenantId);
await InitializeUsers(tenantId);
await InitializeMenus(tenantId);
}
finally
{
// 恢复原租户
App.SetTenantId(originalTenantId);
}
}
}
6.2 租户缓存
实现租户级别的缓存:
csharp
public class TenantCacheService
{
private readonly ICache _cache;
public TenantCacheService(ICache cache)
{
_cache = cache;
}
public T GetTenantCache<T>(string key)
{
var tenantId = App.GetTenantId();
var cacheKey = $"Tenant:{tenantId}:{key}";
return _cache.Get<T>(cacheKey);
}
public void SetTenantCache<T>(string key, T value, int expireSeconds = 3600)
{
var tenantId = App.GetTenantId();
var cacheKey = $"Tenant:{tenantId}:{key}";
_cache.Set(cacheKey, value, expireSeconds);
}
}
6.3 租户配置
支持租户级别的配置管理:
csharp
public class TenantConfigService
{
public T GetTenantConfig<T>(string configKey)
{
var tenantId = App.GetTenantId();
// 从数据库查询租户配置
var config = _db.Queryable<TenantConfigEntity>()
.Where(c => c.TenantId == tenantId && c.ConfigKey == configKey)
.First();
return JsonConvert.DeserializeObject<T>(config.ConfigValue);
}
}
6.4 租户统计
统计租户的数据:
csharp
public class TenantStatisticsService
{
public async Task<TenantStatistics> GetTenantStatistics(string tenantId)
{
var originalTenantId = App.GetTenantId();
App.SetTenantId(tenantId);
try
{
var userCount = await _db.Queryable<UserInfo>().CountAsync();
var orderCount = await _db.Queryable<OrderInfo>().CountAsync();
return new TenantStatistics
{
TenantId = tenantId,
UserCount = userCount,
OrderCount = orderCount,
StatisticTime = DateTime.Now
};
}
finally
{
App.SetTenantId(originalTenantId);
}
}
}
6.5 租户备份与恢复
支持租户数据的备份与恢复:
csharp
public class TenantBackupService
{
public async Task<string> BackupTenant(string tenantId)
{
var originalTenantId = App.GetTenantId();
App.SetTenantId(tenantId);
try
{
// 导出租户数据
var backupData = await ExportTenantData(tenantId);
// 保存备份文件
var backupPath = Path.Combine("backups", $"{tenantId}_{DateTime.Now:yyyyMMddHHmmss}.json");
await File.WriteAllTextAsync(backupPath, backupData);
return backupPath;
}
finally
{
App.SetTenantId(originalTenantId);
}
}
public async Task RestoreTenant(string tenantId, string backupPath)
{
var backupData = await File.ReadAllTextAsync(backupPath);
await ImportTenantData(tenantId, backupData);
}
}
6.6 多系统支持
框架支持多系统和多租户的组合:
json
{
"Tenant": {
"MultiTenancy": true,
"MultiSystem": true
}
}
七、总结
租户管理通过多租户架构和数据隔离机制,实现了灵活的多租户支持。核心设计思想:
- 多租户模式:支持列级隔离和数据库级隔离两种模式
- 自动租户过滤:框架自动为查询添加租户过滤器,无需手动处理
- 租户解析:支持多种租户解析方式,灵活适配不同场景
- 租户配置:支持租户级别的配置管理
- 多系统支持:支持多系统和多租户的组合
这种设计使得应用可以同时服务多个独立的租户,每个租户拥有独立的数据和配置,提高了应用的可扩展性和运营效率。