first commit

This commit is contained in:
Dhananjaya99
2026-09-28 11:44:26 +05:30
parent 721743ae6b
commit bffc9b1686
424 changed files with 46474 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
using Fitness_core_BE.Infra.Tenancy;
using Fitness_core_BE.Interfaces;
namespace Fitness_core_BE.Services
{
/// <summary>
/// Periodically locks (and un-locks) member fingerprint access based on each client's
/// configurable Client.LockAfterDaysOverdue setting (req 7). Reuses the existing
/// AccessDeviceService.LockCustomer/UnlockCustomer (BlackListMember/WhiteListMember)
/// pipeline — this job only decides *who* should be locked, not *how*.
///
/// This is a background service with no HttpContext, so TenantConnectionResolver's normal
/// per-request tenant resolution can never apply here -- a DI-scoped TenantDbContext always
/// resolves to the same single fallback schema regardless of which client is being checked.
/// Every client's overdue-check therefore opens its OWN schema directly via
/// TenantSchemaIterator instead of using the scope's injected TenantDbContext.
///
/// The per-client algorithm itself lives in OverdueLockRunner so
/// AdminBackgroundJobsController's manual "trigger now" endpoint can call the exact same
/// code path instead of duplicating it -- this class only owns the 6-hour loop and the
/// central-only client lookup.
/// </summary>
public class OverdueLockJob : BackgroundService
{
private static readonly TimeSpan Interval = TimeSpan.FromHours(6);
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<OverdueLockJob> _logger;
public OverdueLockJob(IServiceScopeFactory scopeFactory, ILogger<OverdueLockJob> logger)
{
_scopeFactory = scopeFactory;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await RunOnceAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "OverdueLockJob run failed.");
}
try
{
await Task.Delay(Interval, stoppingToken);
}
catch (TaskCanceledException)
{
// Shutting down.
}
}
}
private async Task RunOnceAsync(CancellationToken ct)
{
using var scope = _scopeFactory.CreateScope();
var iterator = scope.ServiceProvider.GetRequiredService<TenantSchemaIterator>();
var runner = scope.ServiceProvider.GetRequiredService<OverdueLockRunner>();
var now = DateTime.UtcNow;
// Central-only lookup -- safe to use the scope's normal DI-resolved repository,
// since GetClientsWithOverdueLockPolicyAsync only ever touches CentralDbContext.
var bootstrapRepo = scope.ServiceProvider.GetRequiredService<IOverdueLockRepository>();
var clients = await bootstrapRepo.GetClientsWithOverdueLockPolicyAsync(ct);
foreach (var client in clients)
{
try
{
await runner.RunForClientAsync(client, now, iterator, ct);
}
catch (Exception ex)
{
_logger.LogError(ex, "OverdueLockJob failed for client {ClientId}.", client.ClientId);
}
}
}
}
}