70 lines
3.4 KiB
C#
70 lines
3.4 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|