diff --git a/CleanArchitecture.Api/BackgroundServices/SetInactiveUsersService.cs b/CleanArchitecture.Api/BackgroundServices/SetInactiveUsersService.cs new file mode 100644 index 0000000..4988645 --- /dev/null +++ b/CleanArchitecture.Api/BackgroundServices/SetInactiveUsersService.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CleanArchitecture.Domain.Entities; +using CleanArchitecture.Domain.Enums; +using CleanArchitecture.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace CleanArchitecture.Api.BackgroundServices; + +public sealed class SetInactiveUsersService : BackgroundService +{ + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + + public SetInactiveUsersService( + IServiceProvider serviceProvider, + ILogger logger) + { + _serviceProvider = serviceProvider; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + IList inactiveUsers = Array.Empty(); + + try + { + inactiveUsers = await context.Users + .Where(user => + user.LastLoggedinDate < DateTime.UtcNow.AddDays(-30) && + user.Status == UserStatus.Active) + .Take(250) + .ToListAsync(stoppingToken); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while retrieving users to set inactive"); + } + + foreach (var user in inactiveUsers) + { + user.SetInactive(); + } + + try + { + await context.SaveChangesAsync(stoppingToken); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while setting users to inactive"); + } + + await Task.Delay(TimeSpan.FromDays(1), stoppingToken); + } + } +} \ No newline at end of file diff --git a/CleanArchitecture.Api/Program.cs b/CleanArchitecture.Api/Program.cs index b9819e7..332f47a 100644 --- a/CleanArchitecture.Api/Program.cs +++ b/CleanArchitecture.Api/Program.cs @@ -1,3 +1,4 @@ +using CleanArchitecture.Api.BackgroundServices; using CleanArchitecture.Api.Extensions; using CleanArchitecture.Application.Extensions; using CleanArchitecture.Application.gRPC; @@ -48,6 +49,8 @@ builder.Services.AddCommandHandlers(); builder.Services.AddNotificationHandlers(); builder.Services.AddApiUser(); +builder.Services.AddHostedService(); + builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); }); builder.Services.AddLogging(x => x.AddSimpleConsole(console => diff --git a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs index aee5fe6..081689c 100644 --- a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs +++ b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs @@ -68,6 +68,14 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase, return ""; } + + user.SetActive(); + user.SetLastLoggedinDate(DateTimeOffset.Now); + + if (!await CommitAsync()) + { + return ""; + } return BuildToken( user, diff --git a/CleanArchitecture.Domain/Entities/User.cs b/CleanArchitecture.Domain/Entities/User.cs index f33ea9d..292007c 100644 --- a/CleanArchitecture.Domain/Entities/User.cs +++ b/CleanArchitecture.Domain/Entities/User.cs @@ -10,6 +10,8 @@ public class User : Entity public string LastName { get; private set; } public string Password { get; private set; } public UserRole Role { get; private set; } + public UserStatus Status { get; private set; } + public DateTimeOffset? LastLoggedinDate { get; private set; } public string FullName => $"{FirstName}, {LastName}"; @@ -23,7 +25,8 @@ public class User : Entity string firstName, string lastName, string password, - UserRole role) : base(id) + UserRole role, + UserStatus status = UserStatus.Active) : base(id) { Email = email; TenantId = tenantId; @@ -31,6 +34,7 @@ public class User : Entity LastName = lastName; Password = password; Role = role; + Status = status; } public void SetEmail(string email) @@ -62,4 +66,19 @@ public class User : Entity { TenantId = tenantId; } + + public void SetLastLoggedinDate(DateTimeOffset lastLoggedinDate) + { + LastLoggedinDate = lastLoggedinDate; + } + + public void SetInactive() + { + Status = UserStatus.Inactive; + } + + public void SetActive() + { + Status = UserStatus.Active; + } } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Enums/UserStatus.cs b/CleanArchitecture.Domain/Enums/UserStatus.cs new file mode 100644 index 0000000..59866a2 --- /dev/null +++ b/CleanArchitecture.Domain/Enums/UserStatus.cs @@ -0,0 +1,7 @@ +namespace CleanArchitecture.Domain.Enums; + +public enum UserStatus +{ + Active, + Inactive +} \ No newline at end of file