142 lines
4.4 KiB
C#
142 lines
4.4 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using UtopiaCanteenSystem.Data;
|
|
using UtopiaCanteenSystem.Models;
|
|
|
|
namespace UtopiaCanteenSystem.Services;
|
|
|
|
/// <summary>
|
|
/// Resolves current meal session from local SQLite <c>meal_schedule_cache</c>.
|
|
/// </summary>
|
|
public class DbMealSessionResolver : IMealSessionResolver
|
|
{
|
|
public const string OfflineCacheMissingMessage =
|
|
"Meal schedule/menu cache not found. Please sync once while online.";
|
|
|
|
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
|
private readonly TimeSpan _cacheTtl = TimeSpan.FromSeconds(60);
|
|
|
|
private readonly Dictionary<string, (List<MealSchedule> Schedules, DateTime ExpiryUtc)> _cache = new(StringComparer.OrdinalIgnoreCase);
|
|
private readonly object _cacheLock = new();
|
|
|
|
public DbMealSessionResolver(IDbContextFactory<AppDbContext> dbFactory)
|
|
{
|
|
_dbFactory = dbFactory;
|
|
}
|
|
|
|
public bool IsScheduleCacheAvailable()
|
|
{
|
|
using var db = _dbFactory.CreateDbContext();
|
|
return db.MealScheduleCache.Any();
|
|
}
|
|
|
|
public bool IsMenuCacheAvailable()
|
|
{
|
|
using var db = _dbFactory.CreateDbContext();
|
|
return db.MenuItemCache.Any() && db.LunchMenuWeekCache.Any();
|
|
}
|
|
|
|
public bool IsOfflineMealDataAvailable() => IsScheduleCacheAvailable() && IsMenuCacheAvailable();
|
|
|
|
private static MealSession MapMealNameToSession(string? mealName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(mealName))
|
|
return MealSession.None;
|
|
|
|
var name = mealName.Trim();
|
|
|
|
return name.ToLowerInvariant() switch
|
|
{
|
|
"breakfast" => MealSession.Breakfast,
|
|
"sehri" => MealSession.Breakfast,
|
|
"lunch" => MealSession.Lunch,
|
|
"iftari" => MealSession.Lunch,
|
|
"tea" => MealSession.Tea,
|
|
"dinner" => MealSession.Dinner,
|
|
_ => MealSession.None
|
|
};
|
|
}
|
|
|
|
public ResolvedMealSession? GetCurrentSession(DateTime nowLocal, string siteId)
|
|
{
|
|
var normalizedSite = SiteIdHelper.NormalizeSiteId(siteId);
|
|
var schedules = GetSchedulesForSiteCached(normalizedSite);
|
|
var t = nowLocal.TimeOfDay;
|
|
|
|
foreach (var s in schedules)
|
|
{
|
|
if (!TryParseTime(s.StartTime, out var start) || !TryParseTime(s.EndTime, out var end))
|
|
continue;
|
|
|
|
if (t >= start && t < end)
|
|
{
|
|
return new ResolvedMealSession
|
|
{
|
|
Session = MapMealNameToSession(s.MealName),
|
|
MealName = s.MealName ?? string.Empty,
|
|
SiteId = normalizedSite,
|
|
StartTime = s.StartTime ?? string.Empty,
|
|
EndTime = s.EndTime ?? string.Empty
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private List<MealSchedule> GetSchedulesForSiteCached(string siteId)
|
|
{
|
|
lock (_cacheLock)
|
|
{
|
|
if (_cache.TryGetValue(siteId, out var entry) && DateTime.UtcNow < entry.ExpiryUtc)
|
|
return entry.Schedules;
|
|
}
|
|
|
|
var list = LoadSchedulesForSiteFromCache(siteId);
|
|
|
|
lock (_cacheLock)
|
|
{
|
|
_cache[siteId] = (list, DateTime.UtcNow.Add(_cacheTtl));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private List<MealSchedule> LoadSchedulesForSiteFromCache(string siteId)
|
|
{
|
|
var siteIdInt = SiteIdHelper.ToInt(siteId);
|
|
using var db = _dbFactory.CreateDbContext();
|
|
|
|
var rows = db.MealScheduleCache
|
|
.AsNoTracking()
|
|
.Where(x => x.LocationSiteId == siteIdInt)
|
|
.OrderBy(x => x.StartTime)
|
|
.ToList();
|
|
|
|
return rows.Select(MapToMealSchedule).ToList();
|
|
}
|
|
|
|
private static MealSchedule MapToMealSchedule(MealScheduleCache row)
|
|
{
|
|
return new MealSchedule
|
|
{
|
|
Id = row.HrmsId,
|
|
MealName = row.MealName,
|
|
LocationSiteId = SiteIdHelper.ToDisplayString(row.LocationSiteId),
|
|
StartTime = row.StartTime,
|
|
EndTime = row.EndTime,
|
|
CreatedAt = row.CreatedAt ?? DateTime.MinValue,
|
|
UpdatedAt = row.UpdatedAt ?? DateTime.MinValue,
|
|
IsActive = true
|
|
};
|
|
}
|
|
|
|
private static bool TryParseTime(string value, out TimeSpan time)
|
|
{
|
|
time = TimeSpan.Zero;
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
return false;
|
|
return TimeSpan.TryParse(value.Trim(), null, out time)
|
|
|| TimeSpan.TryParse(value.Trim().Replace(".", ":"), null, out time);
|
|
}
|
|
}
|