first commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
using Fitness_core_BE.Infra.Tenancy;
|
||||
using Fitness_core_BE.Infra.UoW;
|
||||
using Fitness_core_BE.Models;
|
||||
using Fitness_core_BE.Repos;
|
||||
|
||||
namespace Fitness_core_BE.Services
|
||||
{
|
||||
// The per-client overdue-lock algorithm, extracted verbatim from OverdueLockJob so the
|
||||
// admin "trigger now" endpoint (AdminBackgroundJobsController) can call the exact same code
|
||||
// path instead of duplicating it. Pure extraction -- no change to the locking logic itself.
|
||||
// Mirrors the SubscriptionRenewalRunner/SubscriptionRenewalJob split.
|
||||
public class OverdueLockRunner(ILogger<OverdueLockRunner> logger)
|
||||
{
|
||||
private readonly ILogger<OverdueLockRunner> _logger = logger;
|
||||
|
||||
public async Task RunForClientAsync(Client client, DateTime now, TenantSchemaIterator iterator, CancellationToken ct)
|
||||
{
|
||||
await using var schemaCtx = await iterator.OpenSchemaForClientAsync(client.ClientId, ct);
|
||||
if (schemaCtx == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"OverdueLockJob: client {ClientId} has a lock policy but no provisioned tenant schema; skipping.",
|
||||
client.ClientId);
|
||||
return;
|
||||
}
|
||||
|
||||
var repository = new OverdueLockRepository(schemaCtx.Tenant, schemaCtx.Central);
|
||||
var accessDeviceService = new AccessDeviceService(
|
||||
new EFUnitOfWork(schemaCtx.Central, schemaCtx.Tenant),
|
||||
new AccessDeviceRepo(schemaCtx.Tenant, schemaCtx.Central),
|
||||
new AttendanceRepository(schemaCtx.Tenant),
|
||||
new DeviceCommandRepo(schemaCtx.Tenant),
|
||||
new PairingCodeRepo(schemaCtx.Central));
|
||||
|
||||
var effectiveDays = client.LockAfterDaysOverdue ?? Client.DefaultLockAfterDaysOverdue;
|
||||
var thresholdDate = now.AddDays(-effectiveDays);
|
||||
|
||||
var overdueCustomerIds = await repository.GetOverdueCustomerIdsAsync(
|
||||
client.ClientId, thresholdDate, ct);
|
||||
|
||||
foreach (var customerId in overdueCustomerIds)
|
||||
{
|
||||
var customer = await repository.GetCustomerByIdAsync(customerId, ct);
|
||||
if (customer == null)
|
||||
continue;
|
||||
var alreadyLocked = await repository.IsCustomerAccessDisabledAsync(
|
||||
client.ClientId, customer.FKUserId, ct);
|
||||
if (alreadyLocked)
|
||||
continue;
|
||||
|
||||
await accessDeviceService.SetCustomerAccessAsync(
|
||||
client.ClientId, customerId, enabled: false, reason: "Overdue");
|
||||
_logger.LogInformation("Auto-locked customer {CustomerId} for client {ClientId} (overdue).", customerId, client.ClientId);
|
||||
}
|
||||
|
||||
// Auto-unlock members who were locked for being overdue but are no longer
|
||||
// (paid, or their overdue invoice fell out of scope) — never touches manual locks.
|
||||
var lockedOverdueCustomerIds =
|
||||
await repository.GetCustomersLockedForOverdueAsync(client.ClientId, ct);
|
||||
|
||||
foreach (var customerId in lockedOverdueCustomerIds.Except(overdueCustomerIds))
|
||||
{
|
||||
await accessDeviceService.SetCustomerAccessAsync(
|
||||
client.ClientId, customerId, enabled: true, reason: null);
|
||||
_logger.LogInformation("Auto-unlocked customer {CustomerId} for client {ClientId} (no longer overdue).", customerId, client.ClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user