0
0
mirror of https://github.com/alex289/CleanArchitecture.git synced 2025-06-30 02:31:08 +00:00

Merge pull request #22 from alex289/feature/background_service

feat: Add background service
This commit is contained in:
Alex 2023-09-01 08:57:52 +02:00 committed by GitHub
commit cb73084965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 302 additions and 2 deletions

View File

@ -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<SetInactiveUsersService> _logger;
public SetInactiveUsersService(
IServiceProvider serviceProvider,
ILogger<SetInactiveUsersService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
IList<User> inactiveUsers = Array.Empty<User>();
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);
}
}
}

View File

@ -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<SetInactiveUsersService>();
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
builder.Services.AddLogging(x => x.AddSimpleConsole(console =>

View File

@ -11,6 +11,7 @@ public sealed class UserViewModel
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public UserRole Role { get; set; }
public UserStatus Status { get; set; }
public static UserViewModel FromUser(User user)
{
@ -20,7 +21,8 @@ public sealed class UserViewModel
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
Role = user.Role
Role = user.Role,
Status = user.Status
};
}
}

View File

@ -68,6 +68,14 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
return "";
}
user.SetActive();
user.SetLastLoggedinDate(DateTimeOffset.Now);
if (!await CommitAsync())
{
return "";
}
return BuildToken(
user,

View File

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

View File

@ -0,0 +1,7 @@
namespace CleanArchitecture.Domain.Enums;
public enum UserStatus
{
Active,
Inactive
}

View File

@ -0,0 +1,138 @@
// <auto-generated />
using System;
using CleanArchitecture.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace CleanArchitecture.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20230901064720_AddUserStatus")]
partial class AddUserStatus
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.10")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true)
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("CleanArchitecture.Domain.Entities.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<bool>("Deleted")
.HasColumnType("bit");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.HasKey("Id");
b.ToTable("Tenants");
b.HasData(
new
{
Id = new Guid("b542bf25-134c-47a2-a0df-84ed14d03c4a"),
Deleted = false,
Name = "Admin Tenant"
});
});
modelBuilder.Entity("CleanArchitecture.Domain.Entities.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<bool>("Deleted")
.HasColumnType("bit");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(320)
.HasColumnType("nvarchar(320)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTimeOffset?>("LastLoggedinDate")
.HasColumnType("datetimeoffset");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Password")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Role")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("Users");
b.HasData(
new
{
Id = new Guid("7e3892c0-9374-49fa-a3fd-53db637a40ae"),
Deleted = false,
Email = "admin@email.com",
FirstName = "Admin",
LastName = "User",
Password = "$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2",
Role = 0,
Status = 0,
TenantId = new Guid("b542bf25-134c-47a2-a0df-84ed14d03c4a")
});
});
modelBuilder.Entity("CleanArchitecture.Domain.Entities.User", b =>
{
b.HasOne("CleanArchitecture.Domain.Entities.Tenant", "Tenant")
.WithMany("Users")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tenant");
});
modelBuilder.Entity("CleanArchitecture.Domain.Entities.Tenant", b =>
{
b.Navigation("Users");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CleanArchitecture.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddUserStatus : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "LastLoggedinDate",
table: "Users",
type: "datetimeoffset",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "Status",
table: "Users",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.UpdateData(
table: "Users",
keyColumn: "Id",
keyValue: new Guid("7e3892c0-9374-49fa-a3fd-53db637a40ae"),
columns: new[] { "LastLoggedinDate", "Status" },
values: new object[] { null, 0 });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LastLoggedinDate",
table: "Users");
migrationBuilder.DropColumn(
name: "Status",
table: "Users");
}
}
}

View File

@ -71,6 +71,9 @@ namespace CleanArchitecture.Infrastructure.Migrations
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTimeOffset?>("LastLoggedinDate")
.HasColumnType("datetimeoffset");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(100)
@ -84,6 +87,9 @@ namespace CleanArchitecture.Infrastructure.Migrations
b.Property<int>("Role")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid>("TenantId")
.HasColumnType("uniqueidentifier");
@ -103,6 +109,7 @@ namespace CleanArchitecture.Infrastructure.Migrations
LastName = "User",
Password = "$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2",
Role = 0,
Status = 0,
TenantId = new Guid("b542bf25-134c-47a2-a0df-84ed14d03c4a")
});
});