188 lines
6.9 KiB
C#
188 lines
6.9 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using UtopiaCanteenSystem.Api;
|
|
using UtopiaCanteenSystem.Data;
|
|
using UtopiaCanteenSystem.Services;
|
|
|
|
namespace UtopiaCanteen.BackendService;
|
|
|
|
public sealed class CanteenBackendWorker : BackgroundService
|
|
{
|
|
private readonly IConfigService _config;
|
|
private readonly DbContextFactory _dbFactory;
|
|
private readonly IHostApplicationLifetime _lifetime;
|
|
private readonly IConfiguration _configuration;
|
|
private CancellationTokenSource? _apiCts;
|
|
private Task? _apiTask;
|
|
private System.Timers.Timer? _cacheSyncTimer;
|
|
private System.Timers.Timer? _orderSyncTimer;
|
|
private int _cacheSyncRunning;
|
|
private int _orderSyncRunning;
|
|
private CanteenBackendServices? _backend;
|
|
|
|
public CanteenBackendWorker(
|
|
IConfigService config,
|
|
DbContextFactory dbFactory,
|
|
IHostApplicationLifetime lifetime,
|
|
IConfiguration configuration)
|
|
{
|
|
_config = config;
|
|
_dbFactory = dbFactory;
|
|
_lifetime = lifetime;
|
|
_configuration = configuration;
|
|
}
|
|
|
|
public override async Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
Logger.Log(new Exception("UtopiaCanteen backend service starting."), "CanteenBackendWorker");
|
|
|
|
using (var db = _dbFactory.CreateDbContext())
|
|
db.EnsureDatabaseCreated();
|
|
|
|
var employeeRfidTagSync = new EmployeeRfidTagSyncService(_dbFactory, _config);
|
|
var mealMenuCacheSync = new MealMenuCacheSyncService(_dbFactory, _config);
|
|
var offlineCacheSync = new OfflineCacheSyncService(employeeRfidTagSync, mealMenuCacheSync);
|
|
var employeeLookup = new EmployeeLookupService(_dbFactory, _config);
|
|
var menuLookup = new MenuLookupService(_dbFactory);
|
|
var mealSessionResolver = new DbMealSessionResolver(_dbFactory);
|
|
var productionSync = new SyncService(_dbFactory, _config);
|
|
var rfid = new RfidService(_dbFactory, _config, employeeLookup, mealSessionResolver, menuLookup, productionSync);
|
|
_backend = new CanteenBackendServices(rfid, offlineCacheSync, productionSync, _config);
|
|
|
|
_apiCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
var listenUrls = _config.GetLocalServerListenUrls();
|
|
var backend = _backend;
|
|
var cts = _apiCts;
|
|
_apiTask = Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay(500, cts.Token).ConfigureAwait(false);
|
|
await CanteenBackendHost.RunAsync(backend, listenUrls, cts.Token).ConfigureAwait(false);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// Shutdown
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Log(ex, "CanteenBackendWorker.ApiHost");
|
|
}
|
|
}, CancellationToken.None);
|
|
|
|
_ = RunStartupCacheSyncAsync(cancellationToken);
|
|
|
|
var cacheMinutes = Math.Max(1, _configuration.GetValue("CacheSyncIntervalMinutes", 15));
|
|
if (!string.IsNullOrWhiteSpace(_config.GetHrmsLookupConnectionString()))
|
|
{
|
|
_cacheSyncTimer = new System.Timers.Timer(TimeSpan.FromMinutes(cacheMinutes).TotalMilliseconds) { AutoReset = true };
|
|
_cacheSyncTimer.Elapsed += async (_, _) => await RunCacheSyncSafeAsync().ConfigureAwait(false);
|
|
_cacheSyncTimer.Start();
|
|
}
|
|
|
|
var orderMinutes = Math.Max(1, _configuration.GetValue("ProductionSyncIntervalMinutes", 1));
|
|
if (_config.GetSyncServiceEnabled() && !string.IsNullOrWhiteSpace(_config.GetMySqlConnectionString()))
|
|
{
|
|
_orderSyncTimer = new System.Timers.Timer(TimeSpan.FromMinutes(orderMinutes).TotalMilliseconds) { AutoReset = true };
|
|
_orderSyncTimer.Elapsed += async (_, _) => await RunOrderSyncSafeAsync().ConfigureAwait(false);
|
|
_orderSyncTimer.Start();
|
|
}
|
|
|
|
await base.StartAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
public override async Task StopAsync(CancellationToken cancellationToken)
|
|
{
|
|
Logger.Log(new Exception("UtopiaCanteen backend service stopping."), "CanteenBackendWorker");
|
|
_cacheSyncTimer?.Stop();
|
|
_orderSyncTimer?.Stop();
|
|
_apiCts?.Cancel();
|
|
if (_apiTask != null)
|
|
{
|
|
try
|
|
{
|
|
await _apiTask.WaitAsync(TimeSpan.FromSeconds(10), cancellationToken).ConfigureAwait(false);
|
|
}
|
|
catch
|
|
{
|
|
// Best-effort
|
|
}
|
|
}
|
|
|
|
_cacheSyncTimer?.Dispose();
|
|
_orderSyncTimer?.Dispose();
|
|
_apiCts?.Dispose();
|
|
await base.StopAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
protected override Task ExecuteAsync(CancellationToken stoppingToken) =>
|
|
Task.Delay(Timeout.Infinite, stoppingToken);
|
|
|
|
private async Task RunStartupCacheSyncAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (_backend == null || string.IsNullOrWhiteSpace(_config.GetHrmsLookupConnectionString()))
|
|
return;
|
|
|
|
for (var i = 0; i < 30 && !cancellationToken.IsCancellationRequested; i++)
|
|
{
|
|
try
|
|
{
|
|
await _backend.RunCacheSyncExclusiveAsync(async () =>
|
|
{
|
|
await _backend.OfflineCacheSync.SyncEmployeeAndMenuCacheAsync().ConfigureAwait(false);
|
|
return true;
|
|
}).ConfigureAwait(false);
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Log(ex, "CanteenBackendWorker.RunStartupCacheSyncAsync");
|
|
}
|
|
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
private async Task RunCacheSyncSafeAsync()
|
|
{
|
|
if (_backend == null || Interlocked.Exchange(ref _cacheSyncRunning, 1) == 1)
|
|
return;
|
|
try
|
|
{
|
|
await _backend.RunCacheSyncExclusiveAsync(async () =>
|
|
{
|
|
await _backend.OfflineCacheSync.SyncEmployeeAndMenuCacheAsync().ConfigureAwait(false);
|
|
return true;
|
|
}).ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Log(ex, "CanteenBackendWorker.RunCacheSyncSafeAsync");
|
|
}
|
|
finally
|
|
{
|
|
Interlocked.Exchange(ref _cacheSyncRunning, 0);
|
|
}
|
|
}
|
|
|
|
private async Task RunOrderSyncSafeAsync()
|
|
{
|
|
if (_backend == null || Interlocked.Exchange(ref _orderSyncRunning, 1) == 1)
|
|
return;
|
|
try
|
|
{
|
|
await _backend.RunOrderSyncExclusiveAsync(async () =>
|
|
{
|
|
await _backend.ProductionSync.SyncNowAsync().ConfigureAwait(false);
|
|
return true;
|
|
}).ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.Log(ex, "CanteenBackendWorker.RunOrderSyncSafeAsync");
|
|
}
|
|
finally
|
|
{
|
|
Interlocked.Exchange(ref _orderSyncRunning, 0);
|
|
}
|
|
}
|
|
}
|