JNPF 租户管理

租户管理

一、核心功能

租户管理是 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
  }
}

七、总结

租户管理通过多租户架构和数据隔离机制,实现了灵活的多租户支持。核心设计思想:

  1. 多租户模式:支持列级隔离和数据库级隔离两种模式
  2. 自动租户过滤:框架自动为查询添加租户过滤器,无需手动处理
  3. 租户解析:支持多种租户解析方式,灵活适配不同场景
  4. 租户配置:支持租户级别的配置管理
  5. 多系统支持:支持多系统和多租户的组合

这种设计使得应用可以同时服务多个独立的租户,每个租户拥有独立的数据和配置,提高了应用的可扩展性和运营效率。

相关推荐
胖头鱼不吃鱼-2 年前
扫盲软件开发工具低代码
低代码·jnpf
胖头鱼不吃鱼-2 年前
低代码助力软件开发
低代码·jnpf
这我可不懂2 年前
低代码(Low-Code)技术简化开发难度,快速搭建应用
低代码·微服务·jnpf
这我可不懂3 年前
JNPF低代码开发平台总体架构介绍
低代码·架构·jnpf·开发平台
胖头鱼不吃鱼-3 年前
JNPF低代码体验情况
低代码·jnpf·应用开发
这我可不懂3 年前
适合前后端开发的可视化编辑器(拖拽控件)
低代码·jnpf
虚无火星车3 年前
低代码选型注意事项
低代码·jnpf
这我可不懂3 年前
低代码可视化工具10分钟完成应用开发
低代码·技术栈·jnpf·low-code
虚无火星车3 年前
低代码软件开发的革命
低代码·jnpf