committed by
GitHub
32 changed files with 917 additions and 12 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