From 4ae697600b1c30cce25342f3152b9cace39f5c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:51:33 +0300 Subject: [PATCH] =?UTF-8?q?SetMaxConcurrentUsers=20yap=C4=B1s=C4=B1=20de?= =?UTF-8?q?=C4=9Fi=C5=9Ftirildi.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DynamicApi/CreateUpdateTenantInput.cs | 1 - .../ListForms/ListFormDynamicApiAppService.cs | 2 - .../Seeds/LanguagesData.json | 6 --- .../Seeds/ListFormSeeder_Saas.cs | 25 ++---------- .../PlatformConsts.cs | 1 - .../PlatformModuleExtensionConfigurator.cs | 1 - .../Extensions/AbpTenantExtensions.cs | 9 ----- .../PlatformEfCoreEntityExtensionMappings.cs | 9 ----- .../20260707105700_Initial.Designer.cs | 5 --- .../Migrations/20260707105700_Initial.cs | 1 - .../PlatformDbContextModelSnapshot.cs | 5 --- .../Identity/PlatformSignInManager.cs | 38 +++++++++++++------ 12 files changed, 30 insertions(+), 73 deletions(-) diff --git a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/DynamicApi/CreateUpdateTenantInput.cs b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/DynamicApi/CreateUpdateTenantInput.cs index fc71a45..076826b 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/ListForms/DynamicApi/CreateUpdateTenantInput.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/ListForms/DynamicApi/CreateUpdateTenantInput.cs @@ -28,6 +28,5 @@ public class CreateUpdateTenantInput public string Email { get; set; } public string Website { get; set; } public string MenuGroup { get; set; } - public int MaxConcurrentUsers { get; set; } } diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs index 862ae94..4428079 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs @@ -254,7 +254,6 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic entity.SetEmail(input.Data.Email); entity.SetWebsite(input.Data.Website); entity.SetMenuGroup(input.Data.MenuGroup); - entity.SetMaxConcurrentUsers(input.Data.MaxConcurrentUsers); await tenantRepository.UpdateAsync(entity, autoSave: true); } @@ -290,7 +289,6 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic entity.SetEmail(input.Data.Email); entity.SetWebsite(input.Data.Website); entity.SetMenuGroup(input.Data.MenuGroup); - entity.SetMaxConcurrentUsers(input.Data.MaxConcurrentUsers); await tenantRepository.UpdateAsync(entity, autoSave: true); } diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json index a085055..c72d995 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json @@ -15132,12 +15132,6 @@ "en": "Format Template", "tr": "Format Şablonu" }, - { - "resourceName": "Platform", - "key": "App.Listform.ListformField.MaxConcurrentUsers", - "en": "Max Concurrent Users", - "tr": "Maksimum Eşzamanlı Kullanıcı" - }, { "resourceName": "Platform", "key": "App.Listform.ListformField.IsVerified", diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs b/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs index 1f90e10..9b0a58b 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs @@ -115,10 +115,9 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency new EditingFormItemDto { Order=5, DataField = "PostalCode", ColSpan=1, EditorType2=EditorTypes.dxTextBox, EditorOptions=EditorOptionValues.ShowClearButton }, new EditingFormItemDto { Order=6, DataField = "Email", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=7, DataField = "Website", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, - new EditingFormItemDto { Order=8, DataField = "MaxConcurrentUsers", ColSpan=1, EditorType2=EditorTypes.dxNumberBox }, - new EditingFormItemDto { Order=9, DataField = "IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox }, - new EditingFormItemDto { Order=10, DataField = "Address1", ColSpan=3, EditorType2=EditorTypes.dxTextArea }, - new EditingFormItemDto { Order=11, DataField = "Address2", ColSpan=3, EditorType2=EditorTypes.dxTextArea }, + new EditingFormItemDto { Order=8, DataField = "IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox }, + new EditingFormItemDto { Order=9, DataField = "Address1", ColSpan=3, EditorType2=EditorTypes.dxTextArea }, + new EditingFormItemDto { Order=10, DataField = "Address2", ColSpan=3, EditorType2=EditorTypes.dxTextArea }, ] } }), @@ -140,8 +139,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency }), FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] { new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value }, - new() { FieldName = "Country", FieldDbType = DbType.String, Value = "Türkiye", CustomValueType = FieldCustomValueTypeEnum.Value }, - new() { FieldName = "MaxConcurrentUsers", FieldDbType = DbType.Int32, Value = "0", CustomValueType = FieldCustomValueTypeEnum.Value } + new() { FieldName = "Country", FieldDbType = DbType.String, Value = "Türkiye", CustomValueType = FieldCustomValueTypeEnum.Value } }), SubFormsJson = JsonSerializer.Serialize(new List() { new { @@ -542,21 +540,6 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false), PivotSettingsJson = DefaultPivotSettingsJson }, - new ListFormField - { - ListFormCode = listForm.ListFormCode, - CultureName = LanguageCodes.En, - SourceDbType = DbType.Int32, - FieldName = "MaxConcurrentUsers", - CaptionName = "App.Listform.ListformField.MaxConcurrentUsers", - Width = 0, - ListOrderNo = 21, - Visible = true, - IsActive = true, - ColumnCustomizationJson = DefaultColumnCustomizationJson, - PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false), - PivotSettingsJson = DefaultPivotSettingsJson - }, ], autoSave: true); #endregion } diff --git a/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs b/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs index f67c5a0..a484fe1 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs @@ -70,7 +70,6 @@ public static class PlatformConsts public const string Email = "Email"; public const string Website = "Website"; public const string MenuGroup = "MenuGroup"; - public const string MaxConcurrentUsers = "MaxConcurrentUsers"; } public static class Branches diff --git a/api/src/Sozsoft.Platform.Domain.Shared/PlatformModuleExtensionConfigurator.cs b/api/src/Sozsoft.Platform.Domain.Shared/PlatformModuleExtensionConfigurator.cs index 606af5d..c0cf95b 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/PlatformModuleExtensionConfigurator.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/PlatformModuleExtensionConfigurator.cs @@ -116,7 +116,6 @@ public static class PlatformModuleExtensionConfigurator tenantConfig.ConfigureTenant(entity => { entity.AddOrUpdateProperty("IsActive"); - entity.AddOrUpdateProperty("MaxConcurrentUsers"); }); }); diff --git a/api/src/Sozsoft.Platform.Domain/Extensions/AbpTenantExtensions.cs b/api/src/Sozsoft.Platform.Domain/Extensions/AbpTenantExtensions.cs index 0aa32ab..7ab3f15 100644 --- a/api/src/Sozsoft.Platform.Domain/Extensions/AbpTenantExtensions.cs +++ b/api/src/Sozsoft.Platform.Domain/Extensions/AbpTenantExtensions.cs @@ -165,14 +165,5 @@ public static class AbpTenantExtensions { return tenant.GetProperty(PlatformConsts.Tenants.MenuGroup); } - - public static void SetMaxConcurrentUsers(this Tenant tenant, int? maxConcurrentUsers) - { - tenant.SetProperty(PlatformConsts.Tenants.MaxConcurrentUsers, maxConcurrentUsers.HasValue ? (object)maxConcurrentUsers.Value : null); - } - public static int? GetMaxConcurrentUsers(this Tenant tenant) - { - return tenant.GetProperty(PlatformConsts.Tenants.MaxConcurrentUsers); - } } diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEfCoreEntityExtensionMappings.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEfCoreEntityExtensionMappings.cs index 13d4ec8..fd5c6cf 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEfCoreEntityExtensionMappings.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformEfCoreEntityExtensionMappings.cs @@ -460,15 +460,6 @@ public static class PlatformEfCoreEntityExtensionMappings } ); - ObjectExtensionManager.Instance - .MapEfCoreProperty( - PlatformConsts.Tenants.MaxConcurrentUsers, - (entityBuilder, propertyBuilder) => - { - propertyBuilder.HasDefaultValue(0); - } - ); - ObjectExtensionManager.Instance .MapEfCoreProperty( PlatformConsts.Permissions.MenuGroup, diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.Designer.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.Designer.cs index 21e3acf..bb84637 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.Designer.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.Designer.cs @@ -8430,11 +8430,6 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("uniqueidentifier") .HasColumnName("LastModifierId"); - b.Property("MaxConcurrentUsers") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - b.Property("MenuGroup") .HasMaxLength(64) .HasColumnType("nvarchar(64)"); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.cs index 2ef9b86..d6f5918 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260707105700_Initial.cs @@ -368,7 +368,6 @@ namespace Sozsoft.Platform.Migrations FaxNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true), Founder = table.Column(type: "nvarchar(max)", nullable: true), IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), - MaxConcurrentUsers = table.Column(type: "int", nullable: true, defaultValue: 0), MenuGroup = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), MobileNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true), OrganizationName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 15b63e6..0f70a4c 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -8427,11 +8427,6 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("uniqueidentifier") .HasColumnName("LastModifierId"); - b.Property("MaxConcurrentUsers") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - b.Property("MenuGroup") .HasMaxLength(64) .HasColumnType("nvarchar(64)"); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSignInManager.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSignInManager.cs index 3e5999c..0f460d1 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSignInManager.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSignInManager.cs @@ -8,6 +8,7 @@ using Sozsoft.Platform.Extensions; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.Abp.Domain.Repositories; @@ -31,6 +32,7 @@ public interface IPlatformSignInManager public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager { + private const string UserLicenseProductName = "Public.products.userLicense"; private readonly IClock clock; private readonly IRepository repositoryIp; private readonly IRepository repositoryWorkHour; @@ -39,6 +41,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager private readonly IIdentitySessionRepository identitySessionRepository; private readonly ICurrentTenant currentTenant; private readonly IUnitOfWorkManager unitOfWorkManager; + private readonly IRepository orderRepository; public PlatformSignInManager( IdentityUserManager userManager, @@ -56,7 +59,8 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager ITenantRepository tenantRepository, IIdentitySessionRepository identitySessionRepository, ICurrentTenant currentTenant, - IUnitOfWorkManager unitOfWorkManager + IUnitOfWorkManager unitOfWorkManager, + IRepository orderRepository ) : base( userManager, contextAccessor, @@ -76,6 +80,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager this.identitySessionRepository = identitySessionRepository; this.currentTenant = currentTenant; this.unitOfWorkManager = unitOfWorkManager; + this.orderRepository = orderRepository; } public async Task PreSignInCheckAsync(IdentityUser user) @@ -273,7 +278,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager } /// - /// Tenant'a tanımlı MaxConcurrentUsers limitini aşmamak için login'i engeller. + /// Tenant'a ait aktif Order'lardaki kullanıcı lisansı limitini aşmamak için login'i engeller. /// AbpSessions tablosundaki aktif oturumları sayar (farklı UserId'lere göre distinct). /// Kullanıcının kendisinin zaten oturumu varsa (refresh senaryosu) yeni concurrent user sayılmaz. /// @@ -284,14 +289,8 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager return true; } - var tenant = await tenantRepository.FindAsync(user.TenantId.Value); - if (tenant == null) - { - return true; - } - - var maxConcurrentUsers = tenant.GetMaxConcurrentUsers(); - if (!maxConcurrentUsers.HasValue || maxConcurrentUsers.Value == 0) + var maxConcurrentUsers = await GetActiveUserLicenseQuantityAsync(user.TenantId.Value); + if (maxConcurrentUsers == 0) { return true; } @@ -311,11 +310,11 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager .Distinct() .Count(); - if (otherActiveUserCount >= maxConcurrentUsers.Value) + if (otherActiveUserCount >= maxConcurrentUsers) { Logger.LogWarning(PlatformEventIds.UserCannotSignInConcurrentUserLimit, "Tenant {TenantId} concurrent user limit of {Limit} reached. Active users: {ActiveCount}.", - user.TenantId, maxConcurrentUsers.Value, otherActiveUserCount); + user.TenantId, maxConcurrentUsers, otherActiveUserCount); return false; } @@ -325,5 +324,20 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager return true; } + private async Task GetActiveUserLicenseQuantityAsync(Guid tenantId) + { + using (currentTenant.Change(null)) + { + var orderQuery = await orderRepository.WithDetailsAsync(order => order.Items); + var activeOrders = await orderQuery + .Where(order => order.TenantId == tenantId && order.IsActive) + .ToListAsync(); + + return activeOrders + .SelectMany(order => order.Items) + .Where(item => item.ProductName == UserLicenseProductName) + .Sum(item => item.Quantity); + } + } }