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 decimal Commission { get; set; }
public string Logo { get; set; } public string Logo { get; set; }
Guid? IMultiTenant.TenantId => TenantId;
public PaymentMethod(string id) public PaymentMethod(string id)
{ {
Id = id; Id = id;

View file

@ -401,7 +401,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
} }
else 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.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.WorkHour)), Prefix.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.HasKey(x => new { x.Id, x.TenantId });
b.Property(x => x.Id).HasMaxLength(64); b.Property(x => x.Id).HasMaxLength(64);
b.Property(x => x.Name).HasMaxLength(64).IsRequired(); b.Property(x => x.Name).HasMaxLength(64).IsRequired();
b.Property(x => x.StartTime).HasMaxLength(8).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.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.PaymentMethod)), Prefix.DbSchema);
b.ConfigureByConvention(); 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.Name).IsRequired().HasMaxLength(64);
b.Property(x => x.Logo).HasMaxLength(32); b.Property(x => x.Logo).HasMaxLength(32);
b.Property(x => x.Commission).HasPrecision(5, 3); b.Property(x => x.Commission).HasPrecision(5, 3);

View file

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

View file

@ -877,7 +877,7 @@ namespace Sozsoft.Platform.Migrations
columns: table => new columns: table => new
{ {
Id = table.Column<string>(type: "nvarchar(450)", nullable: false), 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), 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), 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), Logo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
@ -891,7 +891,7 @@ namespace Sozsoft.Platform.Migrations
}, },
constraints: table => 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( migrationBuilder.CreateTable(
@ -1036,7 +1036,7 @@ namespace Sozsoft.Platform.Migrations
columns: table => new columns: table => new
{ {
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false), 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), Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
StartTime = table.Column<DateTime>(type: "datetime2", maxLength: 8, nullable: false), StartTime = table.Column<DateTime>(type: "datetime2", maxLength: 8, nullable: false),
EndTime = 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 => 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( migrationBuilder.CreateTable(

View file

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

View file

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

View file

@ -47,6 +47,27 @@ platformApiService.interceptors.request.use(async (config) => {
platformApiService.interceptors.response.use( platformApiService.interceptors.response.use(
(response) => response, (response) => response,
async (error) => { 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)) { if (unauthorizedCode.includes(error.response?.status)) {
const { signIn, signOut, setIsRefreshing } = store.getActions().auth const { signIn, signOut, setIsRefreshing } = store.getActions().auth
const { auth } = store.getState() const { auth } = store.getState()

View file

@ -93,6 +93,9 @@ const Login = () => {
setError(result.message) setError(result.message)
} else { } else {
setError('') setError('')
//Tenant belirlenmişse
fetchDataByName(tenantName || '')
} }
if (result.status === 'failed') { if (result.status === 'failed') {
@ -155,9 +158,6 @@ const Login = () => {
setError('') setError('')
setMessage('') setMessage('')
//Tenant belirlenmişse
fetchDataByName(tenantName || '')
// Versiyon kontrolü // Versiyon kontrolü
findUiVersion() findUiVersion()
} }
@ -165,25 +165,41 @@ const Login = () => {
setSubmitting(false) setSubmitting(false)
} }
const fetchDataByName = async (name: string) => { const fetchDataByName = async (name: string, isSubdomain = false) => {
if (name) { if (name) {
const response = await getTenantByNameDetail(name) try {
const response = await getTenantByNameDetail(name)
if (response.data) { if (response.data) {
setTenant({ tenantId: response.data.id, tenantName: response.data.name, menuGroup: response.data.menuGroup }); setTenant({ tenantId: response.data.id, tenantName: response.data.name, menuGroup: response.data.menuGroup });
} else { } else {
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} catch {
setTenant(undefined) setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
} }
} else { } else {
setTenant(undefined) 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() const subDomainName = getSubdomain()
useEffect(() => { useEffect(() => {
if (subDomainName) { if (subDomainName) {
setTenantName(subDomainName) setTenantName(subDomainName)
fetchDataByName(subDomainName) fetchDataByName(subDomainName, true)
} }
}, [subDomainName]) }, [subDomainName])