Browse Source

[fix]使用多租户,支持多地点数据使用不同数据库

master
贾荣国Home 2 years ago
parent
commit
7b11018a55
  1. 16
      WebApiService/src/Win_in.Sfs.Scp.WebApi.Application.Contracts/RouteConsts.cs
  2. 25
      WebApiService/src/Win_in.Sfs.Scp.WebApi.Application/Parts/PartAppService.cs
  3. 1
      WebApiService/src/Win_in.Sfs.Scp.WebApi.Application/Validator.cs
  4. 6
      WebApiService/src/Win_in.Sfs.Scp.WebApi.Domain/Asns/Shipments/MEA.cs
  5. 183
      WebApiService/src/Win_in.Sfs.Scp.WebApi.HttpApi.Host/MultiTenantConnectionStringResolver.cs
  6. 50
      WebApiService/src/Win_in.Sfs.Scp.WebApi.HttpApi.Host/WebApiHttpApiHostModule.cs
  7. 25
      WebApiService/src/Win_in.Sfs.Scp.WebApi.HttpApi.Host/appsettings.json
  8. 9
      WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Parts/TA_PART.cs
  9. 4
      WebApiService/src/Win_in.Sfs.Scp.v1.Domain/PurchaseOrders/TB_PO.cs
  10. 6
      WebApiService/src/Win_in.Sfs.Scp.v1.Domain/PurchaseOrders/TB_PO_DETAIL.cs
  11. 4
      WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Receipts/TB_RECEIVE_DETAIL_QAD.cs
  12. 6
      WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Receipts/TB_RECEIVE_QAD.cs
  13. 7
      WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Suppliers/TA_VENDER.cs
  14. 3
      WebApiService/src/Win_in.Sfs.Scp.v1.EntityFrameworkCore/DbContext/IV1ScpDbContext.cs

16
WebApiService/src/Win_in.Sfs.Scp.WebApi.Application.Contracts/RouteConsts.cs

@ -1,4 +1,7 @@
namespace Win_in.Sfs.Scp.WebApi
using System;
using System.Collections.Generic;
namespace Win_in.Sfs.Scp.WebApi
{
public class RouteConsts
{
@ -8,4 +11,15 @@
public const string Supplier = "api/scp/supplier";
public const string UnplannedReceipt = "api/scp/unplanned-receipt";
}
public class SiteConsts
{
public static readonly Dictionary<string, Guid> SiteDict = new Dictionary<string, Guid>
{
{"T8",Guid.Parse("446a5211-3d72-4339-9adc-845151f8ada0")},
{"T9",Guid.Parse("25388015-ef1c-4355-9c18-f6b6ddbaf89d")}
};
}
}

25
WebApiService/src/Win_in.Sfs.Scp.WebApi.Application/Parts/PartAppService.cs

@ -8,6 +8,7 @@ using System.Threading.Tasks.Dataflow;
using AutoMapper;
using AutoMapper.Configuration;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
@ -27,7 +28,7 @@ namespace Win_in.Sfs.Scp.WebApi
[Route(RouteConsts.Part)]
[ApiExplorerSettings(GroupName = SwaggerGroupConsts.ScpWebApi)]
public class PartAppService :ReadOnlyAppService<Part,PartDTO,Guid, RequestDTO>, IPartAppService
public class PartAppService : ReadOnlyAppService<Part, PartDTO, Guid, RequestDTO>, IPartAppService
{
private readonly IPartRepository _partRepository;
private readonly ITaPartRepository _taPartRepository;
@ -35,8 +36,8 @@ namespace Win_in.Sfs.Scp.WebApi
public PartAppService(
IPartRepository repository
,ITaPartRepository taPartRepository
,IConfiguration configuration
, ITaPartRepository taPartRepository
, IConfiguration configuration
) : base(repository)
{
_partRepository = repository;
@ -49,7 +50,7 @@ namespace Win_in.Sfs.Scp.WebApi
/// </summary>
/// <param name="id">唯一ID(unique ID)</param>
/// <returns></returns>
[HttpGet]
[Route("{id}")]
public override async Task<PartDTO> GetAsync(Guid id)
@ -78,13 +79,13 @@ namespace Win_in.Sfs.Scp.WebApi
[Route("")]
public virtual async Task<ActionResult<PartDTO>> CreateAsync(PartCreateDto partCreateDTO)
{
var entity = ObjectMapper.Map<PartCreateDto, Part>(partCreateDTO);
try
{
Validator.CheckSite(_validSites,entity.Site);
Validator.CheckSite(_validSites,entity.Company);
Validator.CheckSite(_validSites, entity.Site);
Validator.CheckSite(_validSites, entity.Company);
await UpsertTaPartAsync(entity);
await CurrentUnitOfWork.SaveChangesAsync();
}
@ -116,8 +117,14 @@ namespace Win_in.Sfs.Scp.WebApi
{
//使用AutoMapper执行类型转换
var taPart = ObjectMapper.Map<Part, TA_PART>(entity);
//根据传入数据新增或修改SCP数据
await _taPartRepository.UpsertAsync(taPart);
var tenantId = SiteConsts.SiteDict.GetValueOrDefault(taPart.Site);
using (CurrentTenant.Change(tenantId,taPart.Site))
{
taPart.TenantId = tenantId;
//根据传入数据新增或修改SCP数据
await _taPartRepository.UpsertAsync(taPart);
}
}
}
}

1
WebApiService/src/Win_in.Sfs.Scp.WebApi.Application/Validator.cs

@ -26,3 +26,4 @@ public static class Validator
}
}
}

6
WebApiService/src/Win_in.Sfs.Scp.WebApi.Domain/Asns/Shipments/MEA.cs

@ -32,14 +32,14 @@ public class MEA
/// 计量值
/// </summary>
[StringLength(10, MinimumLength = 1)]
public string MEA03 { get; set; }
public string MEA03 { get; set; } = "0";
/// <summary>
/// Unit Of Measurement
/// 计量单位
/// </summary>
[StringLength(2, MinimumLength = 2)]
public string MEA04 { get; set; }
public string MEA04 { get; set; } = "KG";
public string MEA05 { get;}
@ -49,7 +49,7 @@ public class MEA
public string MEA09 { get;}
public string MEA10 { get;}
public MEA(MeaType meaType,decimal meaValue,string uom)
public MEA(MeaType meaType,decimal meaValue =0,string uom="KG")
{
MEA02 = meaType.ToString();
MEA03 = meaValue.ToString();

183
WebApiService/src/Win_in.Sfs.Scp.WebApi.HttpApi.Host/MultiTenantConnectionStringResolver.cs

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.WebApi;
public class AAA:DefaultConnectionStringResolver
{
public AAA(IOptionsSnapshot<AbpDbConnectionOptions> options) : base(options)
{
}
public override Task<string> ResolveAsync(string connectionStringName = null)
{
return base.ResolveAsync(connectionStringName);
}
}
[Dependency(ReplaceServices = true)]
public class MultiTenantConnectionStringResolver : DefaultConnectionStringResolver
{
private readonly ICurrentTenant _currentTenant;
private readonly IServiceProvider _serviceProvider;
public MultiTenantConnectionStringResolver(
IOptionsSnapshot<AbpDbConnectionOptions> options,
ICurrentTenant currentTenant,
IServiceProvider serviceProvider)
: base(options)
{
_currentTenant = currentTenant;
_serviceProvider = serviceProvider;
}
public override async Task<string> ResolveAsync(string connectionStringName = null)
{
if (_currentTenant.Id == null)
{
//No current tenant, fallback to default logic
return await base.ResolveAsync(connectionStringName);
}
var tenant = await FindTenantConfigurationAsync(_currentTenant.Id.Value);
if (tenant == null || tenant.ConnectionStrings.IsNullOrEmpty())
{
//Tenant has not defined any connection string, fallback to default logic
return await base.ResolveAsync(connectionStringName);
}
var tenantDefaultConnectionString = tenant.ConnectionStrings.Default;
//Requesting default connection string...
if (connectionStringName == null ||
connectionStringName == ConnectionStrings.DefaultConnectionStringName)
{
//Return tenant's default or global default
return !tenantDefaultConnectionString.IsNullOrWhiteSpace()
? tenantDefaultConnectionString
: Options.ConnectionStrings.Default;
}
//Requesting specific connection string...
var connString = tenant.ConnectionStrings.GetOrDefault(connectionStringName);
if (!connString.IsNullOrWhiteSpace())
{
//Found for the tenant
return connString;
}
//Fallback to the mapped database for the specific connection string
var database = Options.Databases.GetMappedDatabaseOrNull(connectionStringName);
if (database != null && database.IsUsedByTenants)
{
connString = tenant.ConnectionStrings.GetOrDefault(database.DatabaseName);
if (!connString.IsNullOrWhiteSpace())
{
//Found for the tenant
return connString;
}
}
//Fallback to tenant's default connection string if available
if (!tenantDefaultConnectionString.IsNullOrWhiteSpace())
{
return tenantDefaultConnectionString;
}
return await base.ResolveAsync(connectionStringName);
}
[Obsolete("Use ResolveAsync method.")]
public override string Resolve(string connectionStringName = null)
{
if (_currentTenant.Id == null)
{
//No current tenant, fallback to default logic
return base.Resolve(connectionStringName);
}
var tenant = FindTenantConfiguration(_currentTenant.Id.Value);
if (tenant == null || tenant.ConnectionStrings.IsNullOrEmpty())
{
//Tenant has not defined any connection string, fallback to default logic
return base.Resolve(connectionStringName);
}
var tenantDefaultConnectionString = tenant.ConnectionStrings.Default;
//Requesting default connection string...
if (connectionStringName == null ||
connectionStringName == ConnectionStrings.DefaultConnectionStringName)
{
//Return tenant's default or global default
return !tenantDefaultConnectionString.IsNullOrWhiteSpace()
? tenantDefaultConnectionString
: Options.ConnectionStrings.Default;
}
//Requesting specific connection string...
var connString = tenant.ConnectionStrings.GetOrDefault(connectionStringName);
if (!connString.IsNullOrWhiteSpace())
{
//Found for the tenant
return connString;
}
//Fallback to tenant's default connection string if available
if (!tenantDefaultConnectionString.IsNullOrWhiteSpace())
{
return tenantDefaultConnectionString;
}
//Try to find the specific connection string for given name
var connStringInOptions = Options.ConnectionStrings.GetOrDefault(connectionStringName);
if (!connStringInOptions.IsNullOrWhiteSpace())
{
return connStringInOptions;
}
//Fallback to the global default connection string
var defaultConnectionString = Options.ConnectionStrings.Default;
if (!defaultConnectionString.IsNullOrWhiteSpace())
{
return defaultConnectionString;
}
throw new AbpException("No connection string defined!");
}
protected virtual async Task<TenantConfiguration> FindTenantConfigurationAsync(Guid tenantId)
{
using (var serviceScope = _serviceProvider.CreateScope())
{
var tenantStore = serviceScope
.ServiceProvider
.GetRequiredService<ITenantStore>();
return await tenantStore.FindAsync(tenantId);
}
}
[Obsolete("Use FindTenantConfigurationAsync method.")]
protected virtual TenantConfiguration FindTenantConfiguration(Guid tenantId)
{
using (var serviceScope = _serviceProvider.CreateScope())
{
var tenantStore = serviceScope
.ServiceProvider
.GetRequiredService<ITenantStore>();
return tenantStore.Find(tenantId);
}
}
}

50
WebApiService/src/Win_in.Sfs.Scp.WebApi.HttpApi.Host/WebApiHttpApiHostModule.cs

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Win_in.Sfs.Scp.WebApi.EntityFrameworkCore;
using Win_in.Sfs.Scp.WebApi.MultiTenancy;
@ -35,6 +36,9 @@ using Volo.Abp.Swashbuckle;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Win_in.Sfs.Scp.v1.EntityFrameworkCore;
using Volo.Abp.MultiTenancy;
using Volo.Abp.MultiTenancy.ConfigurationStore;
using Volo.Abp.Data;
namespace Win_in.Sfs.Scp.WebApi
{
@ -48,7 +52,8 @@ namespace Win_in.Sfs.Scp.WebApi
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpAccountWebIdentityServerModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpSwashbuckleModule)
typeof(AbpSwashbuckleModule),
typeof(AbpMultiTenancyModule)
)]
[DependsOn(
typeof(V1ScpEntityFrameworkCoreModule))]
@ -59,7 +64,6 @@ namespace Win_in.Sfs.Scp.WebApi
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
ConfigureBundles();
ConfigureUrls(configuration);
@ -69,8 +73,46 @@ namespace Win_in.Sfs.Scp.WebApi
ConfigureVirtualFileSystem(context);
ConfigureCors(context, configuration);
ConfigureSwaggerServices(context, configuration);
ConfigureMultiTenancy(configuration);
ConfigureAuthorization(context,configuration);
context.Services.AddScoped<IConnectionStringResolver, MultiTenantConnectionStringResolver>();
}
private void ConfigureMultiTenancy(IConfiguration configuration)
{
Configure<AbpMultiTenancyOptions>(options => { options.IsEnabled = true; });
// Configure<AbpDefaultTenantStoreOptions>(configuration);
// Configure<AbpDefaultTenantStoreOptions>(options =>
// {
// options.Tenants = new[]
// {
// new TenantConfiguration(
// Guid.Parse("446a5211-3d72-4339-9adc-845151f8ada0"), //Id
// "T8" //Name
// )
// {
// //tenant1 有单独的数据库连接字符串
// ConnectionStrings =
// {
// {V1ScpConsts.ConnectionStringName, "Server=127.0.0.1;Database=SCP_HZ;User ID=sa;Password=Microsoft2008;connection timeout=600;"}
// }
// }
// ,
// new TenantConfiguration(
// Guid.Parse("25388015-ef1c-4355-9c18-f6b6ddbaf89d"), //Id
// "T9" //Name
// )
// {
// //tenant2 有单独的数据库连接字符串
// ConnectionStrings =
// {
// {V1ScpConsts.ConnectionStringName, "Server=127.0.0.1;Database=SCP_TJ;User ID=sa;Password=Microsoft2008;connection timeout=600;"}
// }
// }
// };
// });
}
private static void ConfigureAuthorization(ServiceConfigurationContext context, IConfiguration configuration)
@ -334,4 +376,6 @@ namespace Win_in.Sfs.Scp.WebApi
app.UseConfiguredEndpoints();
}
}
}

25
WebApiService/src/Win_in.Sfs.Scp.WebApi.HttpApi.Host/appsettings.json

@ -5,9 +5,9 @@
"RedirectAllowedUrls": "https://localhost:9977,,https://localhost:9988"
},
"ConnectionStrings": {
"Default": "Server=127.0.0.1;Database=Scp_WebApi;User ID=sa;Password=Microsoft2008;connection timeout=600;",
//"V1Scp": "Server=127.0.0.1;Database=Scp_V1;User ID=sa;Password=Microsoft2008;connection timeout=600;",
"V1Scp": "Server=121.36.30.37,13317;Database=SCP_JZJH;User ID=sa;Password=Microsoft2008;connection timeout=600;"
"Default": "Server=127.0.0.1;Database=Scp_WebApi;User ID=sa;Password=Microsoft2008;connection timeout=600;"
//"V1Scp": "Server=127.0.0.1;Database=SCP_HZ;User ID=sa;Password=Microsoft2008;connection timeout=600;"
},
"AuthServer": {
"Authority": "https://localhost:9988",
@ -34,6 +34,23 @@
}
}
},
"Tenants": [
{
"Id": "446a5211-3d72-4339-9adc-845151f8ada0",
"Name": "T8",
"ConnectionStrings": {
"V1Scp": "Server=127.0.0.1;Database=SCP_HZ;User ID=sa;Password=Microsoft2008;connection timeout=600;"
}
},
{
"Id": "25388015-ef1c-4355-9c18-f6b6ddbaf89d",
"Name": "T9",
"ConnectionStrings": {
"V1Scp": "Server=127.0.0.1;Database=SCP_TJ;User ID=sa;Password=Microsoft2008;connection timeout=600;"
}
}
],
"AlwaysAllowAuthorization": true,
"ValidSites": "T8,JZ1"
"ValidSites": "T8,T9"
}

9
WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Parts/TA_PART.cs

@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.v1.Domain
{
public class TA_PART:Entity<long>
public class TA_PART:Entity<long>,IMultiTenant
{
public string PartCode { get; set; }
@ -39,6 +41,7 @@ namespace Win_in.Sfs.Scp.v1.Domain
public bool? Ischeck { get; set; }
public Guid? TenantId { get; set; }
}
}

4
WebApiService/src/Win_in.Sfs.Scp.v1.Domain/PurchaseOrders/TB_PO.cs

@ -2,10 +2,11 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.v1.Domain
{
public class TB_PO : Entity<long>
public class TB_PO : Entity<long>,IMultiTenant
{
public string PoBillNum { get; set; }
public string ErpBillNum { get; set; }
@ -31,5 +32,6 @@ namespace Win_in.Sfs.Scp.v1.Domain
public bool IsDeleted { get; set; }
public Guid GUID { get; set; }
public Guid? TenantId { get; }
}
}

6
WebApiService/src/Win_in.Sfs.Scp.v1.Domain/PurchaseOrders/TB_PO_DETAIL.cs

@ -2,10 +2,11 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.v1.Domain
{
public class TB_PO_DETAIL : Entity<long>
public class TB_PO_DETAIL : Entity<long>,IMultiTenant
{
public string PoBillNum { get; set; }
@ -40,6 +41,7 @@ namespace Win_in.Sfs.Scp.v1.Domain
public bool IsDeleted { get; set; }
public Guid GUID { get; set; }
public Guid? TenantId { get; }
}
}

4
WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Receipts/TB_RECEIVE_DETAIL_QAD.cs

@ -2,10 +2,11 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.v1.Domain
{
public class TB_RECEIVE_DETAIL_QAD:Entity<long>
public class TB_RECEIVE_DETAIL_QAD:Entity<long>,IMultiTenant
{
public string RecvBillNum { get; set; }
public string PoBillNum { get; set; }
@ -42,5 +43,6 @@ namespace Win_in.Sfs.Scp.v1.Domain
public string Extend2 { get; set; }
public string Extend3 { get; set; }
public string SubSite { get; set; }
public Guid? TenantId { get; }
}
}

6
WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Receipts/TB_RECEIVE_QAD.cs

@ -2,10 +2,11 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.v1.Domain
{
public class TB_RECEIVE_QAD:Entity<long>
public class TB_RECEIVE_QAD:Entity<long>,IMultiTenant
{
public string RecvBillNum { get; set; }
@ -29,6 +30,7 @@ namespace Win_in.Sfs.Scp.v1.Domain
public string Extend3 { get; set; }
public string SubSite { get; set; }
public Guid? TenantId { get; }
}
}

7
WebApiService/src/Win_in.Sfs.Scp.v1.Domain/Suppliers/TA_VENDER.cs

@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Win_in.Sfs.Scp.v1.Domain
{
public class TA_VENDER:Entity<int>
public class TA_VENDER:Entity<int>,IMultiTenant
{
public string VendId { get; set; }
@ -45,5 +47,6 @@ namespace Win_in.Sfs.Scp.v1.Domain
public decimal? Quota { set; get; }
public Guid? TenantId { get; }
}
}

3
WebApiService/src/Win_in.Sfs.Scp.v1.EntityFrameworkCore/DbContext/IV1ScpDbContext.cs

@ -1,9 +1,10 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Win_in.Sfs.Scp.v1.Domain;
namespace Win_in.Sfs.Scp.v1.EntityFrameworkCore
{
public interface IV1ScpDbContext
public interface IV1ScpDbContext:IEfCoreDbContext
{
DbSet<TA_PART> Parts { get; set; }
DbSet<TA_VENDER> Suppliers { get; set; }

Loading…
Cancel
Save