🚀 ABP vNext 多租戶系統實現登錄頁自定義 Logo 的最佳實踐
🧭 版本信息與運行環境
- ABP Framework:v8.1.5
- .NET SDK:8.0
- 數據庫:PostgreSQL(支持 SQLServer、MySQL 等)
- BLOB 存儲:本地/OSS/Azure Blob(推薦啟用 CDN)
- 部署建議:支持單庫/多庫多租戶結構,推薦搭配 CI/CD 自動遷移
📚 目錄
- 🚀 ABP vNext 多租戶系統實現登錄頁自定義 Logo 的最佳實踐
- 🧭 版本信息與運行環境
- 🔍 實現背景
- 📦 總體思路與流程圖
- 🧱 步驟一:擴展租戶實體添加 Logo 字段
- 🔧 實體擴展配置流程
- 🖼 步驟二:上傳并持久化租戶 Logo
- 📥 Logo 上傳與持久化流程
- 🌐 步驟三:登錄頁動態加載租戶 Logo
- ?? 登錄頁 Logo 加載流程
- 🏗? CI/CD 自動遷移流程
- 🧠 最佳實踐建議
🔍 實現背景
ABP vNext 提供完備的多租戶能力,支持根據請求自動切換上下文。在實際項目中,為每個租戶提供個性化登錄 Logo 是增強品牌感、提升用戶體驗的重要方式。
📦 總體思路與流程圖
使用 ABP 的實體擴展和 Blob 模塊,結合租戶解析機制,動態加載 Logo:
🧱 步驟一:擴展租戶實體添加 Logo 字段
🔧 實體擴展配置流程
💡 推薦位置:*.Domain.Shared
+ *.Domain
// MyTenantConsts.cs
public static class MyTenantConsts
{public const string LogoUrlPropertyName = "LogoUrl";
}
// MyTenantEntityExtension.cs
public static class MyTenantEntityExtension
{public static void Configure(){ObjectExtensionManager.Instance.Modules().ConfigureSaas(sa =>{sa.ConfigureTenant(t =>{t.AddOrUpdateProperty<string>(MyTenantConsts.LogoUrlPropertyName);});});}
}
在模塊類中 PreConfigureServices
階段調用:
public override void PreConfigureServices(ServiceConfigurationContext context)
{MyTenantEntityExtension.Configure();
}
? 確保遷移腳本包含新字段
🖼 步驟二:上傳并持久化租戶 Logo
📥 Logo 上傳與持久化流程
💡 建議封裝至 Application Service:
public class TenantUiAppService : ApplicationService
{private readonly IBlobContainer _blobContainer;private readonly ITenantRepository _tenantRepository;public TenantUiAppService(IBlobContainer blobContainer, ITenantRepository tenantRepository){_blobContainer = blobContainer;_tenantRepository = tenantRepository;}public async Task<string> UploadLogoAsync(Guid tenantId, IFormFile file){if (file.Length > 1024 * 1024)throw new UserFriendlyException("文件不能超過 1MB");if (!file.ContentType.StartsWith("image/"))throw new UserFriendlyException("請上傳 PNG 或 JPG 圖片");var blobPath = $"{tenantId}/logo.png";using var stream = file.OpenReadStream();await _blobContainer.SaveAsync(blobPath, stream, overrideExisting: true);var tenant = await _tenantRepository.GetAsync(tenantId);tenant.SetProperty(MyTenantConsts.LogoUrlPropertyName, blobPath);await _tenantRepository.UpdateAsync(tenant); // 持久化return await _blobContainer.GetDownloadUrlAsync(blobPath); // 帶簽名 URL}
}
🌐 步驟三:登錄頁動態加載租戶 Logo
?? 登錄頁 Logo 加載流程
封裝服務接口:
public class TenantUiQueryService : ApplicationService
{private readonly ITenantRepository _tenantRepository;private readonly IBlobContainer _blobContainer;public TenantUiQueryService(ITenantRepository tenantRepository, IBlobContainer blobContainer){_tenantRepository = tenantRepository;_blobContainer = blobContainer;}public async Task<string> GetLogoUrlAsync(){var tenantId = CurrentTenant.Id;if (tenantId == null)return "/images/default-logo.png";var tenant = await _tenantRepository.GetAsync(tenantId.Value);if (!tenant.ExtraProperties.TryGetValue(MyTenantConsts.LogoUrlPropertyName, out var rawPath) ||rawPath == null){return "/images/default-logo.png";}return await _blobContainer.GetDownloadUrlAsync(rawPath.ToString());}
}
前端渲染:
<img src="@logoUrl" onerror="this.src='/images/default-logo.png'" class="login-logo" />
🏗? CI/CD 自動遷移流程
🧠 最佳實踐建議
分類 | 建議 |
---|---|
性能 | 租戶 Logo 可使用 IDistributedCache 緩存 |
用戶體驗 | 加載失敗 fallback 默認圖 |
可擴展性 | 建議將租戶個性化信息封裝至 TenantUiCustomization 模塊 |
安全 | Blob 下載 URL 推薦使用帶簽名的臨時訪問 |
發布 | 配合數據庫遷移工具(Flyway、DbUp) |