IMultiTenant

This commit is contained in:
Sedat ÖZTÜRK 2026-03-10 16:42:16 +03:00
parent dbd3e9f32a
commit 35006880bd
9 changed files with 87 additions and 41 deletions

View file

@ -12,6 +12,8 @@ public class PaymentMethod : FullAuditedEntity<string>, IMultiTenant
public decimal Commission { get; set; }
public string Logo { get; set; }
Guid? IMultiTenant.TenantId => TenantId;
public PaymentMethod(string id)
{
Id = id;

View file

@ -401,7 +401,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
}
else
{
whereParts.Add($"\"TenantId\" IS NULL");
whereParts.Add($"(\"TenantId\" IS NULL OR \"TenantId\" = '{Guid.Empty}')");
}
}

View file

@ -756,6 +756,7 @@ public class PlatformDbContext :
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.WorkHour)), Prefix.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new { x.Id, x.TenantId });
b.Property(x => x.Id).HasMaxLength(64);
b.Property(x => x.Name).HasMaxLength(64).IsRequired();
b.Property(x => x.StartTime).HasMaxLength(8).IsRequired();
@ -824,8 +825,8 @@ public class PlatformDbContext :
{
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.PaymentMethod)), Prefix.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new { x.Id, x.TenantId });
b.HasKey(x => x.Id);
b.Property(x => x.Name).IsRequired().HasMaxLength(64);
b.Property(x => x.Logo).HasMaxLength(32);
b.Property(x => x.Commission).HasPrecision(5, 3);

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260302191746_Initial")]
[Migration("20260310134051_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -3166,6 +3166,10 @@ namespace Sozsoft.Platform.Migrations
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<decimal>("Commission")
.HasPrecision(5, 3)
.HasColumnType("decimal(5,3)");
@ -3209,11 +3213,7 @@ namespace Sozsoft.Platform.Migrations
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasKey("Id", "TenantId");
b.ToTable("Adm_T_PaymentMethod", (string)null);
});
@ -3894,6 +3894,10 @@ namespace Sozsoft.Platform.Migrations
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -3949,10 +3953,6 @@ namespace Sozsoft.Platform.Migrations
b.Property<bool?>("Sunday")
.HasColumnType("bit");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<bool?>("Thursday")
.HasColumnType("bit");
@ -3962,7 +3962,7 @@ namespace Sozsoft.Platform.Migrations
b.Property<bool?>("Wednesday")
.HasColumnType("bit");
b.HasKey("Id");
b.HasKey("Id", "TenantId");
b.ToTable("Adm_T_WorkHour", (string)null);
});

View file

@ -877,7 +877,7 @@ namespace Sozsoft.Platform.Migrations
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Commission = table.Column<decimal>(type: "decimal(5,3)", precision: 5, scale: 3, nullable: false),
Logo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
@ -891,7 +891,7 @@ namespace Sozsoft.Platform.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_Adm_T_PaymentMethod", x => x.Id);
table.PrimaryKey("PK_Adm_T_PaymentMethod", x => new { x.Id, x.TenantId });
});
migrationBuilder.CreateTable(
@ -1036,7 +1036,7 @@ namespace Sozsoft.Platform.Migrations
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
StartTime = table.Column<DateTime>(type: "datetime2", maxLength: 8, nullable: false),
EndTime = table.Column<DateTime>(type: "datetime2", maxLength: 8, nullable: false),
@ -1057,7 +1057,7 @@ namespace Sozsoft.Platform.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_Adm_T_WorkHour", x => x.Id);
table.PrimaryKey("PK_Adm_T_WorkHour", x => new { x.Id, x.TenantId });
});
migrationBuilder.CreateTable(

View file

@ -3163,6 +3163,10 @@ namespace Sozsoft.Platform.Migrations
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<decimal>("Commission")
.HasPrecision(5, 3)
.HasColumnType("decimal(5,3)");
@ -3206,11 +3210,7 @@ namespace Sozsoft.Platform.Migrations
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasKey("Id", "TenantId");
b.ToTable("Adm_T_PaymentMethod", (string)null);
});
@ -3891,6 +3891,10 @@ namespace Sozsoft.Platform.Migrations
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
@ -3946,10 +3950,6 @@ namespace Sozsoft.Platform.Migrations
b.Property<bool?>("Sunday")
.HasColumnType("bit");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<bool?>("Thursday")
.HasColumnType("bit");
@ -3959,7 +3959,7 @@ namespace Sozsoft.Platform.Migrations
b.Property<bool?>("Wednesday")
.HasColumnType("bit");
b.HasKey("Id");
b.HasKey("Id", "TenantId");
b.ToTable("Adm_T_WorkHour", (string)null);
});

View file

@ -13,6 +13,7 @@ using Microsoft.EntityFrameworkCore;
using Volo.Abp.Identity;
using Volo.Abp.Timing;
using System.Collections.Generic;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.Platform.Data.Seeds;
@ -267,6 +268,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
private readonly OrganizationUnitManager _organizationUnitManager;
private readonly IIdentityUserRepository _repositoryUser;
private readonly IRepository<Product, Guid> _productRepository;
private readonly ICurrentTenant _currentTenant;
public TenantDataSeeder(
IClock clock,
@ -294,7 +296,8 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
IRepository<ForumCategory, Guid> forumCategoryRepository,
IRepository<Currency, string> currencyRepository,
IRepository<OrganizationUnit, Guid> organizationUnitRepository,
OrganizationUnitManager organizationUnitManager
OrganizationUnitManager organizationUnitManager,
ICurrentTenant currentTenant
)
{
_clock = clock;
@ -323,6 +326,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
_currencyRepository = currencyRepository;
_organizationUnitRepository = organizationUnitRepository;
_organizationUnitManager = organizationUnitManager;
_currentTenant = currentTenant;
}
public async Task SeedAsync(DataSeedContext context)
@ -488,7 +492,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
foreach (var item in items.PaymentMethods)
{
var exists = await _paymentMethodRepository.AnyAsync(x => x.Name == item.Name);
var exists = await _paymentMethodRepository.AnyAsync(x => x.Id == item.Name);
if (!exists)
{
@ -496,8 +500,9 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
{
Name = item.Name,
Commission = item.Commission,
Logo = item.Logo
});
Logo = item.Logo,
TenantId = _currentTenant.Id ?? Guid.Empty
}, autoSave: true);
}
}
@ -619,12 +624,13 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
foreach (var item in items.WorkHours)
{
var exists = await _workHourRepository.AnyAsync(x => x.Name == item.Name);
var exists = await _workHourRepository.AnyAsync(x => x.Id == item.Name);
if (!exists)
{
await _workHourRepository.InsertAsync(new WorkHour(item.Name)
{
TenantId = _currentTenant.Id ?? Guid.Empty,
Name = item.Name,
StartTime = item.StartTime,
EndTime = item.EndTime,

View file

@ -47,6 +47,27 @@ platformApiService.interceptors.request.use(async (config) => {
platformApiService.interceptors.response.use(
(response) => response,
async (error) => {
// Geçersiz tenant seçilmişse Host olarak tekrar bağlan.
// __tenant header'ı gönderilmiş bir isteğe 404 geliyorsa tenant bulunamadı demektir.
const hasTenantHeader = !!error.config?.headers?.['__tenant']
const responseText = JSON.stringify(error.response?.data ?? '').toLowerCase()
const isTenantNotFound =
error.response?.status === 404 &&
hasTenantHeader &&
responseText.includes('tenant')
console.log('Found', isTenantNotFound)
console.log('Error', error.response?.data?.error?.code)
if (isTenantNotFound && !error.config._tenantRetried) {
store.getActions().locale.setTenantName(undefined)
error.config._tenantRetried = true
error.config.headers.delete('__tenant')
error.silent = true
return platformApiService.request(error.config)
}
if (unauthorizedCode.includes(error.response?.status)) {
const { signIn, signOut, setIsRefreshing } = store.getActions().auth
const { auth } = store.getState()

View file

@ -93,6 +93,9 @@ const Login = () => {
setError(result.message)
} else {
setError('')
//Tenant belirlenmişse
fetchDataByName(tenantName || '')
}
if (result.status === 'failed') {
@ -155,9 +158,6 @@ const Login = () => {
setError('')
setMessage('')
//Tenant belirlenmişse
fetchDataByName(tenantName || '')
// Versiyon kontrolü
findUiVersion()
}
@ -165,25 +165,41 @@ const Login = () => {
setSubmitting(false)
}
const fetchDataByName = async (name: string) => {
const fetchDataByName = async (name: string, isSubdomain = false) => {
if (name) {
try {
const response = await getTenantByNameDetail(name)
if (response.data) {
setTenant({ tenantId: response.data.id, tenantName: response.data.name, menuGroup: response.data.menuGroup });
} else {
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} catch {
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} else {
setTenant(undefined)
}
}
const redirectToMainDomain = (name: string) => {
setTenantName(undefined)
const parts = window.location.hostname.split('.')
const mainDomain = parts.length >= 3 ? parts.slice(1).join('.') : window.location.hostname
setWarningTimeout(`"${name}" kurumuna ait kayıt bulunamadı. Ana sayfaya yönlendiriliyorsunuz...`)
setTimeout(() => {
window.location.href = `${window.location.protocol}//${mainDomain}`
}, 3000)
}
const subDomainName = getSubdomain()
useEffect(() => {
if (subDomainName) {
setTenantName(subDomainName)
fetchDataByName(subDomainName)
fetchDataByName(subDomainName, true)
}
}, [subDomainName])