SetMaxConcurrentUsers yapısı değiştirildi.

This commit is contained in:
Sedat ÖZTÜRK 2026-07-07 17:51:33 +03:00
parent 4fd22fe682
commit 4ae697600b
12 changed files with 30 additions and 73 deletions

View file

@ -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; }
}

View file

@ -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);
}

View file

@ -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",

View file

@ -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<dynamic>() {
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
}

View file

@ -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

View file

@ -116,7 +116,6 @@ public static class PlatformModuleExtensionConfigurator
tenantConfig.ConfigureTenant(entity =>
{
entity.AddOrUpdateProperty<bool>("IsActive");
entity.AddOrUpdateProperty<int?>("MaxConcurrentUsers");
});
});

View file

@ -165,14 +165,5 @@ public static class AbpTenantExtensions
{
return tenant.GetProperty<string>(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<int?>(PlatformConsts.Tenants.MaxConcurrentUsers);
}
}

View file

@ -460,15 +460,6 @@ public static class PlatformEfCoreEntityExtensionMappings
}
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<Tenant, int?>(
PlatformConsts.Tenants.MaxConcurrentUsers,
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<PermissionDefinitionRecord, string>(
PlatformConsts.Permissions.MenuGroup,

View file

@ -8430,11 +8430,6 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<int?>("MaxConcurrentUsers")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<string>("MenuGroup")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");

View file

@ -368,7 +368,6 @@ namespace Sozsoft.Platform.Migrations
FaxNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
Founder = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
MaxConcurrentUsers = table.Column<int>(type: "int", nullable: true, defaultValue: 0),
MenuGroup = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
MobileNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
OrganizationName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),

View file

@ -8427,11 +8427,6 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<int?>("MaxConcurrentUsers")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<string>("MenuGroup")
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");

View file

@ -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<IpRestriction, Guid> repositoryIp;
private readonly IRepository<WorkHour, Guid> 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<Order, Guid> 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<Order, Guid> 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<bool> PreSignInCheckAsync(IdentityUser user)
@ -273,7 +278,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
}
/// <summary>
/// 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.
/// </summary>
@ -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<int> 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);
}
}
}