SetMaxConcurrentUsers yapısı değiştirildi.
This commit is contained in:
parent
4fd22fe682
commit
4ae697600b
12 changed files with 30 additions and 73 deletions
|
|
@ -28,6 +28,5 @@ public class CreateUpdateTenantInput
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string Website { get; set; }
|
public string Website { get; set; }
|
||||||
public string MenuGroup { get; set; }
|
public string MenuGroup { get; set; }
|
||||||
public int MaxConcurrentUsers { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,6 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
|
||||||
entity.SetEmail(input.Data.Email);
|
entity.SetEmail(input.Data.Email);
|
||||||
entity.SetWebsite(input.Data.Website);
|
entity.SetWebsite(input.Data.Website);
|
||||||
entity.SetMenuGroup(input.Data.MenuGroup);
|
entity.SetMenuGroup(input.Data.MenuGroup);
|
||||||
entity.SetMaxConcurrentUsers(input.Data.MaxConcurrentUsers);
|
|
||||||
|
|
||||||
await tenantRepository.UpdateAsync(entity, autoSave: true);
|
await tenantRepository.UpdateAsync(entity, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +289,6 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
|
||||||
entity.SetEmail(input.Data.Email);
|
entity.SetEmail(input.Data.Email);
|
||||||
entity.SetWebsite(input.Data.Website);
|
entity.SetWebsite(input.Data.Website);
|
||||||
entity.SetMenuGroup(input.Data.MenuGroup);
|
entity.SetMenuGroup(input.Data.MenuGroup);
|
||||||
entity.SetMaxConcurrentUsers(input.Data.MaxConcurrentUsers);
|
|
||||||
|
|
||||||
await tenantRepository.UpdateAsync(entity, autoSave: true);
|
await tenantRepository.UpdateAsync(entity, autoSave: true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15132,12 +15132,6 @@
|
||||||
"en": "Format Template",
|
"en": "Format Template",
|
||||||
"tr": "Format Şablonu"
|
"tr": "Format Şablonu"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"resourceName": "Platform",
|
|
||||||
"key": "App.Listform.ListformField.MaxConcurrentUsers",
|
|
||||||
"en": "Max Concurrent Users",
|
|
||||||
"tr": "Maksimum Eşzamanlı Kullanıcı"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.Listform.ListformField.IsVerified",
|
"key": "App.Listform.ListformField.IsVerified",
|
||||||
|
|
|
||||||
|
|
@ -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=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=6, DataField = "Email", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order=7, DataField = "Website", 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=8, DataField = "IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
|
||||||
new EditingFormItemDto { Order=9, DataField = "IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
|
new EditingFormItemDto { Order=9, DataField = "Address1", ColSpan=3, EditorType2=EditorTypes.dxTextArea },
|
||||||
new EditingFormItemDto { Order=10, DataField = "Address1", ColSpan=3, EditorType2=EditorTypes.dxTextArea },
|
new EditingFormItemDto { Order=10, DataField = "Address2", ColSpan=3, EditorType2=EditorTypes.dxTextArea },
|
||||||
new EditingFormItemDto { Order=11, DataField = "Address2", ColSpan=3, EditorType2=EditorTypes.dxTextArea },
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -140,8 +139,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
}),
|
}),
|
||||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||||
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
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 = "Country", FieldDbType = DbType.String, Value = "Türkiye", CustomValueType = FieldCustomValueTypeEnum.Value }
|
||||||
new() { FieldName = "MaxConcurrentUsers", FieldDbType = DbType.Int32, Value = "0", CustomValueType = FieldCustomValueTypeEnum.Value }
|
|
||||||
}),
|
}),
|
||||||
SubFormsJson = JsonSerializer.Serialize(new List<dynamic>() {
|
SubFormsJson = JsonSerializer.Serialize(new List<dynamic>() {
|
||||||
new {
|
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),
|
PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false),
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson
|
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);
|
], autoSave: true);
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ public static class PlatformConsts
|
||||||
public const string Email = "Email";
|
public const string Email = "Email";
|
||||||
public const string Website = "Website";
|
public const string Website = "Website";
|
||||||
public const string MenuGroup = "MenuGroup";
|
public const string MenuGroup = "MenuGroup";
|
||||||
public const string MaxConcurrentUsers = "MaxConcurrentUsers";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Branches
|
public static class Branches
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,6 @@ public static class PlatformModuleExtensionConfigurator
|
||||||
tenantConfig.ConfigureTenant(entity =>
|
tenantConfig.ConfigureTenant(entity =>
|
||||||
{
|
{
|
||||||
entity.AddOrUpdateProperty<bool>("IsActive");
|
entity.AddOrUpdateProperty<bool>("IsActive");
|
||||||
entity.AddOrUpdateProperty<int?>("MaxConcurrentUsers");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,14 +165,5 @@ public static class AbpTenantExtensions
|
||||||
{
|
{
|
||||||
return tenant.GetProperty<string>(PlatformConsts.Tenants.MenuGroup);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -460,15 +460,6 @@ public static class PlatformEfCoreEntityExtensionMappings
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ObjectExtensionManager.Instance
|
|
||||||
.MapEfCoreProperty<Tenant, int?>(
|
|
||||||
PlatformConsts.Tenants.MaxConcurrentUsers,
|
|
||||||
(entityBuilder, propertyBuilder) =>
|
|
||||||
{
|
|
||||||
propertyBuilder.HasDefaultValue(0);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ObjectExtensionManager.Instance
|
ObjectExtensionManager.Instance
|
||||||
.MapEfCoreProperty<PermissionDefinitionRecord, string>(
|
.MapEfCoreProperty<PermissionDefinitionRecord, string>(
|
||||||
PlatformConsts.Permissions.MenuGroup,
|
PlatformConsts.Permissions.MenuGroup,
|
||||||
|
|
|
||||||
|
|
@ -8430,11 +8430,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("uniqueidentifier")
|
.HasColumnType("uniqueidentifier")
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<int?>("MaxConcurrentUsers")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasDefaultValue(0);
|
|
||||||
|
|
||||||
b.Property<string>("MenuGroup")
|
b.Property<string>("MenuGroup")
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
.HasColumnType("nvarchar(64)");
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
FaxNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
FaxNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||||
Founder = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
Founder = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: 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),
|
MenuGroup = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
MobileNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
MobileNumber = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||||
OrganizationName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
OrganizationName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
|
|
|
||||||
|
|
@ -8427,11 +8427,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("uniqueidentifier")
|
.HasColumnType("uniqueidentifier")
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<int?>("MaxConcurrentUsers")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasDefaultValue(0);
|
|
||||||
|
|
||||||
b.Property<string>("MenuGroup")
|
b.Property<string>("MenuGroup")
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
.HasColumnType("nvarchar(64)");
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using Sozsoft.Platform.Extensions;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
@ -31,6 +32,7 @@ public interface IPlatformSignInManager
|
||||||
|
|
||||||
public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
{
|
{
|
||||||
|
private const string UserLicenseProductName = "Public.products.userLicense";
|
||||||
private readonly IClock clock;
|
private readonly IClock clock;
|
||||||
private readonly IRepository<IpRestriction, Guid> repositoryIp;
|
private readonly IRepository<IpRestriction, Guid> repositoryIp;
|
||||||
private readonly IRepository<WorkHour, Guid> repositoryWorkHour;
|
private readonly IRepository<WorkHour, Guid> repositoryWorkHour;
|
||||||
|
|
@ -39,6 +41,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
private readonly IIdentitySessionRepository identitySessionRepository;
|
private readonly IIdentitySessionRepository identitySessionRepository;
|
||||||
private readonly ICurrentTenant currentTenant;
|
private readonly ICurrentTenant currentTenant;
|
||||||
private readonly IUnitOfWorkManager unitOfWorkManager;
|
private readonly IUnitOfWorkManager unitOfWorkManager;
|
||||||
|
private readonly IRepository<Order, Guid> orderRepository;
|
||||||
|
|
||||||
public PlatformSignInManager(
|
public PlatformSignInManager(
|
||||||
IdentityUserManager userManager,
|
IdentityUserManager userManager,
|
||||||
|
|
@ -56,7 +59,8 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
ITenantRepository tenantRepository,
|
ITenantRepository tenantRepository,
|
||||||
IIdentitySessionRepository identitySessionRepository,
|
IIdentitySessionRepository identitySessionRepository,
|
||||||
ICurrentTenant currentTenant,
|
ICurrentTenant currentTenant,
|
||||||
IUnitOfWorkManager unitOfWorkManager
|
IUnitOfWorkManager unitOfWorkManager,
|
||||||
|
IRepository<Order, Guid> orderRepository
|
||||||
) : base(
|
) : base(
|
||||||
userManager,
|
userManager,
|
||||||
contextAccessor,
|
contextAccessor,
|
||||||
|
|
@ -76,6 +80,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
this.identitySessionRepository = identitySessionRepository;
|
this.identitySessionRepository = identitySessionRepository;
|
||||||
this.currentTenant = currentTenant;
|
this.currentTenant = currentTenant;
|
||||||
this.unitOfWorkManager = unitOfWorkManager;
|
this.unitOfWorkManager = unitOfWorkManager;
|
||||||
|
this.orderRepository = orderRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> PreSignInCheckAsync(IdentityUser user)
|
public async Task<bool> PreSignInCheckAsync(IdentityUser user)
|
||||||
|
|
@ -273,7 +278,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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).
|
/// 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.
|
/// Kullanıcının kendisinin zaten oturumu varsa (refresh senaryosu) yeni concurrent user sayılmaz.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -284,14 +289,8 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tenant = await tenantRepository.FindAsync(user.TenantId.Value);
|
var maxConcurrentUsers = await GetActiveUserLicenseQuantityAsync(user.TenantId.Value);
|
||||||
if (tenant == null)
|
if (maxConcurrentUsers == 0)
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxConcurrentUsers = tenant.GetMaxConcurrentUsers();
|
|
||||||
if (!maxConcurrentUsers.HasValue || maxConcurrentUsers.Value == 0)
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -311,11 +310,11 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Count();
|
.Count();
|
||||||
|
|
||||||
if (otherActiveUserCount >= maxConcurrentUsers.Value)
|
if (otherActiveUserCount >= maxConcurrentUsers)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(PlatformEventIds.UserCannotSignInConcurrentUserLimit,
|
Logger.LogWarning(PlatformEventIds.UserCannotSignInConcurrentUserLimit,
|
||||||
"Tenant {TenantId} concurrent user limit of {Limit} reached. Active users: {ActiveCount}.",
|
"Tenant {TenantId} concurrent user limit of {Limit} reached. Active users: {ActiveCount}.",
|
||||||
user.TenantId, maxConcurrentUsers.Value, otherActiveUserCount);
|
user.TenantId, maxConcurrentUsers, otherActiveUserCount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,5 +324,20 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue