28 changed files with 890 additions and 5 deletions
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /><GeneratePackageOnBuild>true</GeneratePackageOnBuild> |
|||
<Version>2.9.0</Version> |
|||
<Authors>LINGYUN</Authors> |
|||
<Description>阿里云Oss对象存储Abp集成</Description> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<OutputPath>D:\LocalNuget</OutputPath> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Qiniu.Shared" Version="7.2.15" /> |
|||
<PackageReference Include="Volo.Abp.BlobStoring" Version="2.9.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
[DependsOn(typeof(AbpBlobStoringModule))] |
|||
public class AbpBlobStoringQiniuModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
public class DefaultQiniuBlobNameCalculator : IQiniuBlobNameCalculator, ITransientDependency |
|||
{ |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
|
|||
public DefaultQiniuBlobNameCalculator( |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
} |
|||
|
|||
public string Calculate(BlobProviderArgs args) |
|||
{ |
|||
return CurrentTenant.Id == null |
|||
? $"host/{args.BlobName}" |
|||
: $"tenants/{CurrentTenant.Id.Value:D}/{args.BlobName}"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
public interface IQiniuBlobNameCalculator |
|||
{ |
|||
string Calculate(BlobProviderArgs args); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
public static class QiniuBlobContainerConfigurationExtensions |
|||
{ |
|||
public static QiniuBlobProviderConfiguration GetQiniuConfiguration( |
|||
this BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
return new QiniuBlobProviderConfiguration(containerConfiguration); |
|||
} |
|||
|
|||
public static BlobContainerConfiguration UseQiniu( |
|||
this BlobContainerConfiguration containerConfiguration, |
|||
Action<QiniuBlobProviderConfiguration> qiniuConfigureAction) |
|||
{ |
|||
containerConfiguration.ProviderType = typeof(QiniuBlobProvider); |
|||
|
|||
qiniuConfigureAction(new QiniuBlobProviderConfiguration(containerConfiguration)); |
|||
|
|||
return containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
using Qiniu.Util; |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.DependencyInjection; |
|||
using QiniuConfig = Qiniu.Common.Config; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
public class QiniuBlobProvider : BlobProviderBase, ITransientDependency |
|||
{ |
|||
protected IQiniuBlobNameCalculator QiniuBlobNameCalculator { get; } |
|||
public override Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override Task SaveAsync(BlobProviderSaveArgs args) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
private Mac GetMac(BlobProviderArgs args) |
|||
{ |
|||
var configuration = args.Configuration.GetQiniuConfiguration(); |
|||
return new Mac(configuration.AccessKey, configuration.SecretKey); |
|||
} |
|||
|
|||
private string GetAndInitBucketName(BlobProviderArgs args) |
|||
{ |
|||
var configuration = args.Configuration.GetQiniuConfiguration(); |
|||
var bucketName = configuration.BucketName.IsNullOrWhiteSpace() |
|||
? args.ContainerName |
|||
: configuration.BucketName; |
|||
QiniuConfig.AutoZone(configuration.AccessKey, bucketName, true); |
|||
return bucketName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
public class QiniuBlobProviderConfiguration |
|||
{ |
|||
/// <summary>
|
|||
/// Api授权码
|
|||
/// </remarks>
|
|||
public string AccessKey |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(QiniuBlobProviderConfigurationNames.AccessKey); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.AccessKey, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
/// <summary>
|
|||
/// Api密钥
|
|||
/// </summary>
|
|||
public string SecretKey |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(QiniuBlobProviderConfigurationNames.SecretKey); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.SecretKey, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
/// <summary>
|
|||
/// 默认的Bucket名称
|
|||
/// </summary>
|
|||
public string BucketName |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(QiniuBlobProviderConfigurationNames.BucketName, ""); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.BucketName, value ?? ""); |
|||
} |
|||
/// <summary>
|
|||
/// 默认自动删除该文件天数
|
|||
/// 默认 0,不删除
|
|||
/// </summary>
|
|||
public int DeleteAfterDays |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(QiniuBlobProviderConfigurationNames.DeleteAfterDays, 0); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.DeleteAfterDays, value); |
|||
} |
|||
/// <summary>
|
|||
/// 上传成功后,七牛云向业务服务器发送 POST 请求的 URL。
|
|||
/// 必须是公网上可以正常进行 POST 请求并能响应 HTTP/1.1 200 OK 的有效 URL
|
|||
/// </summary>
|
|||
public string UploadCallbackUrl |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(QiniuBlobProviderConfigurationNames.UploadCallbackUrl, ""); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.UploadCallbackUrl, value ?? ""); |
|||
} |
|||
/// <summary>
|
|||
/// 上传成功后,七牛云向业务服务器发送回调通知时的 Host 值。
|
|||
/// 与 callbackUrl 配合使用,仅当设置了 callbackUrl 时才有效。
|
|||
/// </summary>
|
|||
public string UploadCallbackHost |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(QiniuBlobProviderConfigurationNames.UploadCallbackHost, ""); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.UploadCallbackHost, value ?? ""); |
|||
} |
|||
/// <summary>
|
|||
/// 上传成功后,七牛云向业务服务器发送回调通知 callbackBody 的 Content-Type。
|
|||
/// 默认为 application/x-www-form-urlencoded,也可设置为 application/json。
|
|||
/// </summary>
|
|||
public string UploadCallbackBodyType |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(QiniuBlobProviderConfigurationNames.UploadCallbackBodyType, ""); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.UploadCallbackBodyType, value ?? ""); |
|||
} |
|||
/// <summary>
|
|||
/// 上传成功后,自定义七牛云最终返回給上传端(在指定 returnUrl 时是携带在跳转路径参数中)的数据。
|
|||
/// 支持魔法变量和自定义变量。returnBody 要求是合法的 JSON 文本。
|
|||
/// 例如 {"key": $(key), "hash": $(etag), "w": $(imageInfo.width), "h": $(imageInfo.height)}。
|
|||
/// </summary>
|
|||
public string UploadCallbackBody |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(QiniuBlobProviderConfigurationNames.UploadCallbackBody, ""); |
|||
set => _containerConfiguration.SetConfiguration(QiniuBlobProviderConfigurationNames.UploadCallbackBody, value ?? ""); |
|||
} |
|||
private readonly BlobContainerConfiguration _containerConfiguration; |
|||
|
|||
public QiniuBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
_containerConfiguration = containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
namespace LINGYUN.Abp.BlobStoring.Qiniu |
|||
{ |
|||
public static class QiniuBlobProviderConfigurationNames |
|||
{ |
|||
/// <summary>
|
|||
/// Api授权码
|
|||
/// </summary>
|
|||
public const string AccessKey = "Qiniu:OSS:AccessKey"; |
|||
/// <summary>
|
|||
/// Api密钥
|
|||
/// </summary>
|
|||
public const string SecretKey = "Qiniu:OSS:SecretKey"; |
|||
/// <summary>
|
|||
/// 默认的Bucket名称
|
|||
/// </summary>
|
|||
public const string BucketName = "Qiniu:OSS:BucketName"; |
|||
/// <summary>
|
|||
/// 默认自动删除该文件天数
|
|||
/// 默认 0,不删除
|
|||
/// </summary>
|
|||
public const string DeleteAfterDays = "Qiniu:OSS:DeleteAfterDays"; |
|||
/// <summary>
|
|||
/// 上传成功后,七牛云向业务服务器发送 POST 请求的 URL。
|
|||
/// 必须是公网上可以正常进行 POST 请求并能响应 HTTP/1.1 200 OK 的有效 URL
|
|||
/// </summary>
|
|||
public const string UploadCallbackUrl = "Qiniu:OSS:UploadCallbackUrl"; |
|||
/// <summary>
|
|||
/// 上传成功后,七牛云向业务服务器发送回调通知时的 Host 值。
|
|||
/// 与 callbackUrl 配合使用,仅当设置了 callbackUrl 时才有效。
|
|||
/// </summary>
|
|||
public const string UploadCallbackHost = "Qiniu:OSS:UploadCallbackHost"; |
|||
/// <summary>
|
|||
/// 上传成功后,七牛云向业务服务器发送回调通知 callbackBody 的 Content-Type。
|
|||
/// 默认为 application/x-www-form-urlencoded,也可设置为 application/json。
|
|||
/// </summary>
|
|||
public const string UploadCallbackBodyType = "Qiniu:OSS:UploadCallbackBodyType"; |
|||
/// <summary>
|
|||
/// 上传成功后,自定义七牛云最终返回給上传端(在指定 returnUrl 时是携带在跳转路径参数中)的数据。
|
|||
/// 支持魔法变量和自定义变量。returnBody 要求是合法的 JSON 文本。
|
|||
/// 例如 {"key": $(key), "hash": $(etag), "w": $(imageInfo.width), "h": $(imageInfo.height)}。
|
|||
/// </summary>
|
|||
public const string UploadCallbackBody = "Qiniu:OSS:UploadCallbackBody"; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> |
|||
<Version>2.9.0</Version> |
|||
<Authors>LINGYUN</Authors> |
|||
<Company /> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<OutputPath>D:\LocalNuget</OutputPath> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Caching" Version="2.9.0" /> |
|||
<PackageReference Include="Volo.Abp.TenantManagement.Domain" Version="2.9.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.TenantManagement; |
|||
|
|||
namespace LINGYUN.Abp.MultiTenancy.DbFinder |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpCachingModule), |
|||
typeof(AbpTenantManagementDomainModule) |
|||
)] |
|||
public class AbpDbFinderMultiTenancyModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace LINGYUN.Abp.MultiTenancy.DbFinder |
|||
{ |
|||
public class TenantConfigurationCacheItem |
|||
{ |
|||
public Guid Id { get; set; } |
|||
public string Name { get; set; } |
|||
// TODO: 是否需要加密存储?
|
|||
public ConnectionStrings ConnectionStrings { get; set; } = new ConnectionStrings(); |
|||
|
|||
public TenantConfigurationCacheItem() { } |
|||
|
|||
public TenantConfigurationCacheItem(Guid id, string name, ConnectionStrings connectionStrings) |
|||
{ |
|||
Id = id; |
|||
Name = name; |
|||
ConnectionStrings = connectionStrings ?? ConnectionStrings; |
|||
} |
|||
public static string CalculateCacheKey(string key) |
|||
{ |
|||
return "p:tenant" + ",k:" + key; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,146 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.TenantManagement; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.MultiTenancy.DbFinder |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(ITenantStore))] |
|||
public class TenantStore : ITenantStore |
|||
{ |
|||
public ILogger<TenantStore> Logger { protected get; set; } |
|||
private readonly IDistributedCache<TenantConfigurationCacheItem> _cache; |
|||
|
|||
private readonly ITenantRepository _tenantRepository; |
|||
|
|||
public TenantStore( |
|||
ITenantRepository tenantRepository, |
|||
IDistributedCache<TenantConfigurationCacheItem> cache) |
|||
{ |
|||
_cache = cache; |
|||
_tenantRepository = tenantRepository; |
|||
|
|||
Logger = NullLogger<TenantStore>.Instance; |
|||
} |
|||
|
|||
public virtual TenantConfiguration Find(string name) |
|||
{ |
|||
var tenantCacheItem = AsyncHelper.RunSync(async () => await |
|||
GetCacheItemByNameAsync(name)); |
|||
|
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
public virtual TenantConfiguration Find(Guid id) |
|||
{ |
|||
var tenantCacheItem = AsyncHelper.RunSync(async () => await |
|||
GetCacheItemByIdAsync(id)); |
|||
|
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
public virtual async Task<TenantConfiguration> FindAsync(string name) |
|||
{ |
|||
var tenantCacheItem = await GetCacheItemByNameAsync(name); |
|||
|
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
public virtual async Task<TenantConfiguration> FindAsync(Guid id) |
|||
{ |
|||
var tenantCacheItem = await GetCacheItemByIdAsync(id); |
|||
|
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
protected virtual async Task<TenantConfigurationCacheItem> GetCacheItemByIdAsync(Guid id) |
|||
{ |
|||
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(id.ToString()); |
|||
|
|||
Logger.LogDebug($"TenantStore.GetCacheItemByIdAsync: {cacheKey}"); |
|||
|
|||
var cacheItem = await _cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem != null) |
|||
{ |
|||
Logger.LogDebug($"Found in the cache: {cacheKey}"); |
|||
return cacheItem; |
|||
} |
|||
Logger.LogDebug($"Not found in the cache, getting from the repository: {cacheKey}"); |
|||
|
|||
var tenant = await _tenantRepository.FindAsync(id, true); |
|||
if (tenant == null) |
|||
{ |
|||
Logger.LogWarning($"Can not found tenant by id: {id}"); |
|||
throw new AbpException($"Can not found tenant by id: {id}"); |
|||
} |
|||
var connectionStrings = new ConnectionStrings(); |
|||
foreach (var tenantConnectionString in tenant.ConnectionStrings) |
|||
{ |
|||
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; |
|||
} |
|||
cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings); |
|||
|
|||
Logger.LogDebug($"Setting the cache item: {cacheKey}"); |
|||
await _cache.SetAsync(cacheKey, cacheItem); |
|||
Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); |
|||
|
|||
return cacheItem; |
|||
} |
|||
protected virtual async Task<TenantConfigurationCacheItem> GetCacheItemByNameAsync(string name) |
|||
{ |
|||
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(name); |
|||
|
|||
Logger.LogDebug($"TenantStore.GetCacheItemByNameAsync: {cacheKey}"); |
|||
|
|||
var cacheItem = await _cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem != null) |
|||
{ |
|||
Logger.LogDebug($"Found in the cache: {cacheKey}"); |
|||
return cacheItem; |
|||
} |
|||
Logger.LogDebug($"Not found in the cache, getting from the repository: {cacheKey}"); |
|||
|
|||
var tenant = await _tenantRepository.FindByNameAsync(name); |
|||
if (tenant == null) |
|||
{ |
|||
Logger.LogWarning($"Can not found tenant by name: {name}"); |
|||
throw new AbpException($"Can not found tenant by name: {name}"); |
|||
} |
|||
var connectionStrings = new ConnectionStrings(); |
|||
foreach (var tenantConnectionString in tenant.ConnectionStrings) |
|||
{ |
|||
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; |
|||
} |
|||
cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings); |
|||
|
|||
Logger.LogDebug($"Setting the cache item: {cacheKey}"); |
|||
await _cache.SetAsync(cacheKey, cacheItem); |
|||
Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); |
|||
|
|||
return cacheItem; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
# LINGYUN.Abp.MultiTenancy.DbFinder |
|||
|
|||
abp 多租户数据库查询组件,引用此模块将首先从分布式缓存查询当前租户配置 |
|||
|
|||
如果缓存不存在,则调用租户仓储接口获取租户数据,并存储到分布式缓存中 |
|||
|
|||
## 配置使用 |
|||
|
|||
模块按需引用 |
|||
|
|||
启动项目需要引用**Volo.Abp.TenantManagement.EntityFrameworkCore** |
|||
|
|||
``` shell |
|||
// .NET CLI |
|||
dotnet add package Volo.Abp.TenantManagement.EntityFrameworkCore --version 2.9.0 |
|||
|
|||
// Package Manager |
|||
Install-Package Volo.Abp.TenantManagement.EntityFrameworkCore -Version 2.9.0 |
|||
|
|||
``` |
|||
|
|||
事先定义**appsettings.json**文件 |
|||
|
|||
```json |
|||
{ |
|||
"ConnectionStrings": { |
|||
"AbpTenantManagement": "Server=127.0.0.1;Database=TenantDb;User Id=root;Password=yourPassword" |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpDbFinderMultiTenancyModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
``` |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> |
|||
<Version>2.9.0</Version> |
|||
<Authors>LINGYUN</Authors> |
|||
<Company /> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<OutputPath>D:\LocalNuget</OutputPath> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Caching" Version="2.9.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TenantManagement.HttpApi.Client\LINGYUN.Abp.TenantManagement.HttpApi.Client.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using LINGYUN.Abp.TenantManagement; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.MultiTenancy.RemoteService |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpCachingModule), |
|||
typeof(AbpTenantManagementHttpApiClientModule))] |
|||
public class AbpRemoteServiceMultiTenancyModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace LINGYUN.Abp.MultiTenancy.RemoteService |
|||
{ |
|||
public class TenantConfigurationCacheItem |
|||
{ |
|||
public Guid Id { get; set; } |
|||
public string Name { get; set; } |
|||
// TODO: 是否需要加密存储?
|
|||
public ConnectionStrings ConnectionStrings { get; set; } = new ConnectionStrings(); |
|||
|
|||
public TenantConfigurationCacheItem() { } |
|||
|
|||
public TenantConfigurationCacheItem(Guid id, string name, ConnectionStrings connectionStrings) |
|||
{ |
|||
Id = id; |
|||
Name = name; |
|||
ConnectionStrings = connectionStrings ?? ConnectionStrings; |
|||
} |
|||
public static string CalculateCacheKey(string key) |
|||
{ |
|||
return "p:tenant" + ",k:" + key; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
using LINGYUN.Abp.TenantManagement; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.ObjectMapping; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.MultiTenancy.RemoteService |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] |
|||
[ExposeServices(typeof(ITenantStore))] |
|||
public class TenantStore : ITenantStore |
|||
{ |
|||
public ILogger<TenantStore> Logger { protected get; set; } |
|||
private readonly IDistributedCache<TenantConfigurationCacheItem> _cache; |
|||
|
|||
private readonly ITenantAppService _tenantAppService; |
|||
public TenantStore( |
|||
ITenantAppService tenantAppService, |
|||
IDistributedCache<TenantConfigurationCacheItem> cache) |
|||
{ |
|||
_cache = cache; |
|||
_tenantAppService = tenantAppService; |
|||
|
|||
Logger = NullLogger<TenantStore>.Instance; |
|||
} |
|||
public virtual TenantConfiguration Find(string name) |
|||
{ |
|||
var tenantCacheItem = AsyncHelper.RunSync(async () => await |
|||
GetCacheItemByNameAsync(name)); |
|||
|
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
public virtual TenantConfiguration Find(Guid id) |
|||
{ |
|||
var tenantCacheItem = AsyncHelper.RunSync(async () => await |
|||
GetCacheItemByIdAsync(id)); |
|||
|
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
public virtual async Task<TenantConfiguration> FindAsync(string name) |
|||
{ |
|||
var tenantCacheItem = await GetCacheItemByNameAsync(name); |
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
public virtual async Task<TenantConfiguration> FindAsync(Guid id) |
|||
{ |
|||
var tenantCacheItem = await GetCacheItemByIdAsync(id); |
|||
return new TenantConfiguration(tenantCacheItem.Id, tenantCacheItem.Name) |
|||
{ |
|||
ConnectionStrings = tenantCacheItem.ConnectionStrings |
|||
}; |
|||
} |
|||
|
|||
protected virtual async Task<TenantConfigurationCacheItem> GetCacheItemByIdAsync(Guid id) |
|||
{ |
|||
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(id.ToString()); |
|||
|
|||
Logger.LogDebug($"TenantStore.GetCacheItemByIdAsync: {cacheKey}"); |
|||
|
|||
var cacheItem = await _cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem != null) |
|||
{ |
|||
Logger.LogDebug($"Found in the cache: {cacheKey}"); |
|||
return cacheItem; |
|||
} |
|||
Logger.LogDebug($"Not found in the cache, getting from the remote service: {cacheKey}"); |
|||
|
|||
var tenantDto = await _tenantAppService.GetAsync(id); |
|||
var tenantConnectionStringsDto = await _tenantAppService.GetConnectionStringAsync(id); |
|||
var connectionStrings = new ConnectionStrings(); |
|||
foreach (var tenantConnectionString in tenantConnectionStringsDto.Items) |
|||
{ |
|||
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; |
|||
} |
|||
cacheItem = new TenantConfigurationCacheItem(tenantDto.Id, tenantDto.Name, connectionStrings); |
|||
|
|||
Logger.LogDebug($"Setting the cache item: {cacheKey}"); |
|||
await _cache.SetAsync(cacheKey, cacheItem); |
|||
Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); |
|||
|
|||
return cacheItem; |
|||
} |
|||
protected virtual async Task<TenantConfigurationCacheItem> GetCacheItemByNameAsync(string name) |
|||
{ |
|||
var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(name); |
|||
|
|||
Logger.LogDebug($"TenantStore.GetCacheItemByNameAsync: {cacheKey}"); |
|||
|
|||
var cacheItem = await _cache.GetAsync(cacheKey); |
|||
|
|||
if (cacheItem != null) |
|||
{ |
|||
Logger.LogDebug($"Found in the cache: {cacheKey}"); |
|||
return cacheItem; |
|||
} |
|||
Logger.LogDebug($"Not found in the cache, getting from the remote service: {cacheKey}"); |
|||
|
|||
var tenantDto = await _tenantAppService.GetAsync(new TenantGetByNameInputDto(name)); |
|||
var tenantConnectionStringsDto = await _tenantAppService.GetConnectionStringAsync(tenantDto.Id); |
|||
var connectionStrings = new ConnectionStrings(); |
|||
foreach(var tenantConnectionString in tenantConnectionStringsDto.Items) |
|||
{ |
|||
connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; |
|||
} |
|||
cacheItem = new TenantConfigurationCacheItem(tenantDto.Id, tenantDto.Name, connectionStrings); |
|||
|
|||
Logger.LogDebug($"Setting the cache item: {cacheKey}"); |
|||
await _cache.SetAsync(cacheKey, cacheItem); |
|||
Logger.LogDebug($"Finished setting the cache item: {cacheKey}"); |
|||
|
|||
return cacheItem; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
# LINGYUN.Abp.MultiTenancy.RemoteService |
|||
|
|||
abp 多租户远程服务组件,引用此模块将首先从分布式缓存查询当前租户配置 |
|||
|
|||
如果缓存不存在,则调用远程租户服务接口获取租户数据,并存储到分布式缓存中 |
|||
|
|||
## 配置使用 |
|||
|
|||
模块按需引用,因为远程服务接口定义了授权策略,需要配置接口客户端授权 |
|||
|
|||
具体**RemoteServices**与**IdentityClients**配置请阅读abp文档 |
|||
|
|||
[RemoteServices配置参阅](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients) |
|||
|
|||
[IdentityClients配置参阅](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs) |
|||
|
|||
事先定义**appsettings.json**文件 |
|||
|
|||
```json |
|||
{ |
|||
"RemoteServices": { |
|||
"TenantManagement": { |
|||
"BaseUrl": "http://localhost:30000/", |
|||
"IdentityClient": "tenant-finder-client" |
|||
} |
|||
}, |
|||
"IdentityClients": { |
|||
"tenant-finder-client": { |
|||
"Authority": "http://localhost:44385", |
|||
"RequireHttps": false, |
|||
"GrantType": "client_credentials", |
|||
"ClientId": "tenant-finder-client", |
|||
"Scope": "tenant-service", |
|||
"ClientSecret": "1q2w3e*" |
|||
} |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpRemoteServiceMultiTenancyModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
``` |
|||
@ -0,0 +1,18 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Volo.Abp.TenantManagement; |
|||
|
|||
namespace LINGYUN.Abp.TenantManagement |
|||
{ |
|||
public class TenantGetByNameInputDto |
|||
{ |
|||
[Required] |
|||
[StringLength(TenantConsts.MaxNameLength)] |
|||
public string Name { get; set; } |
|||
|
|||
public TenantGetByNameInputDto() { } |
|||
public TenantGetByNameInputDto(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"TenantNotFoundById": "Tenant: {0} not found!", |
|||
"TenantNotFoundByName": "Tenant: {0} not found!" |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"TenantNotFoundById": "租户: {0} 不存在!", |
|||
"TenantNotFoundByName": "租户: {0} 不存在!" |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> |
|||
<Version>2.9.0</Version> |
|||
<Authors>LINGYUN</Authors> |
|||
<Company /> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<OutputPath>D:\LocalNuget</OutputPath> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Http.Client" Version="2.9.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TenantManagement.Application.Contracts\LINGYUN.Abp.TenantManagement.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,22 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.TenantManagement |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpTenantManagementApplicationContractsModule), |
|||
typeof(AbpHttpClientModule))] |
|||
public class AbpTenantManagementHttpApiClientModule : AbpModule |
|||
{ |
|||
public const string RemoteServiceName = "TenantManagement"; |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(AbpTenantManagementApplicationContractsModule).Assembly, |
|||
RemoteServiceName |
|||
); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue