✨ Notable days (holiday)
This commit is contained in:
53
DysonNetwork.Pass/Account/NotableDay.cs
Normal file
53
DysonNetwork.Pass/Account/NotableDay.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Nager.Holiday;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
/// <summary>
|
||||
/// Reference from Nager.Holiday
|
||||
/// </summary>
|
||||
public enum NotableHolidayType
|
||||
{
|
||||
/// <summary>Public holiday</summary>
|
||||
Public,
|
||||
/// <summary>Bank holiday, banks and offices are closed</summary>
|
||||
Bank,
|
||||
/// <summary>School holiday, schools are closed</summary>
|
||||
School,
|
||||
/// <summary>Authorities are closed</summary>
|
||||
Authorities,
|
||||
/// <summary>Majority of people take a day off</summary>
|
||||
Optional,
|
||||
/// <summary>Optional festivity, no paid day off</summary>
|
||||
Observance,
|
||||
}
|
||||
|
||||
|
||||
public class NotableDay
|
||||
{
|
||||
public Instant Date { get; set; }
|
||||
public string? LocalName { get; set; }
|
||||
public string? GlobalName { get; set; }
|
||||
public string? CountryCode { get; set; }
|
||||
public NotableHolidayType[] Holidays { get; set; } = [];
|
||||
|
||||
public static NotableDay FromNagerHoliday(PublicHoliday holiday)
|
||||
{
|
||||
return new NotableDay()
|
||||
{
|
||||
Date = Instant.FromDateTimeUtc(holiday.Date.ToUniversalTime()),
|
||||
LocalName = holiday.LocalName,
|
||||
GlobalName = holiday.Name,
|
||||
CountryCode = holiday.CountryCode,
|
||||
Holidays = holiday.Types?.Select(x => x switch
|
||||
{
|
||||
PublicHolidayType.Public => NotableHolidayType.Public,
|
||||
PublicHolidayType.Bank => NotableHolidayType.Bank,
|
||||
PublicHolidayType.School => NotableHolidayType.School,
|
||||
PublicHolidayType.Authorities => NotableHolidayType.Authorities,
|
||||
PublicHolidayType.Optional => NotableHolidayType.Optional,
|
||||
_ => NotableHolidayType.Observance
|
||||
}).ToArray() ?? [],
|
||||
};
|
||||
}
|
||||
}
|
51
DysonNetwork.Pass/Account/NotableDaysController.cs
Normal file
51
DysonNetwork.Pass/Account/NotableDaysController.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/notable")]
|
||||
public class NotableDaysController(NotableDaysService days) : ControllerBase
|
||||
{
|
||||
[HttpGet("{regionCode}/{year:int}")]
|
||||
public async Task<ActionResult<List<NotableDay>>> GetRegionDays(string regionCode, int year)
|
||||
{
|
||||
var result = await days.GetNotableDays(year, regionCode);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet("{regionCode}")]
|
||||
public async Task<ActionResult<List<NotableDay>>> GetRegionDaysCurrentYear(string regionCode)
|
||||
{
|
||||
var currentYear = DateTime.Now.Year;
|
||||
var result = await days.GetNotableDays(currentYear, regionCode);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet("me/{year:int}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<NotableDay>>> GetAccountNotableDays(int year)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
var region = currentUser.Region;
|
||||
if (string.IsNullOrWhiteSpace(region)) region = "us";
|
||||
|
||||
var result = await days.GetNotableDays(year, region);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet("me")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<NotableDay>>> GetAccountNotableDaysCurrentYear()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
var currentYear = DateTime.Now.Year;
|
||||
var region = currentUser.Region;
|
||||
if (string.IsNullOrWhiteSpace(region)) region = "us";
|
||||
|
||||
var result = await days.GetNotableDays(currentYear, region);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
34
DysonNetwork.Pass/Account/NotableDaysService.cs
Normal file
34
DysonNetwork.Pass/Account/NotableDaysService.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Nager.Holiday;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
public class NotableDaysService(ICacheService cache)
|
||||
{
|
||||
private const string NotableDaysCacheKeyPrefix = "notable:";
|
||||
|
||||
public async Task<List<NotableDay>> GetNotableDays(int? year, string regionCode)
|
||||
{
|
||||
year ??= DateTime.UtcNow.Year;
|
||||
|
||||
// Generate cache key using year and region code
|
||||
var cacheKey = $"{NotableDaysCacheKeyPrefix}:{year}:{regionCode}";
|
||||
|
||||
// Try to get from cache first
|
||||
var (found, cachedDays) = await cache.GetAsyncWithStatus<List<NotableDay>>(cacheKey);
|
||||
if (found && cachedDays != null)
|
||||
{
|
||||
return cachedDays;
|
||||
}
|
||||
|
||||
// If not in cache, fetch from API
|
||||
using var holidayClient = new HolidayClient();
|
||||
var holidays = await holidayClient.GetHolidaysAsync(year.Value, regionCode);
|
||||
var days = holidays?.Select(NotableDay.FromNagerHoliday).ToList() ?? [];
|
||||
|
||||
// Cache the result for 1 day (holiday data doesn't change frequently)
|
||||
await cache.SetAsync(cacheKey, days, TimeSpan.FromDays(1));
|
||||
|
||||
return days;
|
||||
}
|
||||
}
|
@@ -193,6 +193,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<ActionLogService>();
|
||||
services.AddScoped<AccountService>();
|
||||
services.AddScoped<AccountEventService>();
|
||||
services.AddScoped<NotableDaysService>();
|
||||
services.AddScoped<ActionLogService>();
|
||||
services.AddScoped<RelationshipService>();
|
||||
services.AddScoped<MagicSpellService>();
|
||||
|
Reference in New Issue
Block a user