Event Calendar

This commit is contained in:
LittleSheep 2025-05-09 01:41:09 +08:00
parent b370c69670
commit 4fd8a588fa
3 changed files with 75 additions and 7 deletions

View File

@ -257,7 +257,7 @@ public class AccountController(
.OrderByDescending(s => s.CreatedAt) .OrderByDescending(s => s.CreatedAt)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (status is null) return NotFound(); if (status is null) return NotFound();
await events.ClearStatus(currentUser, status); await events.ClearStatus(currentUser, status);
return NoContent(); return NoContent();
} }
@ -268,31 +268,31 @@ public class AccountController(
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var userId = currentUser.Id; var userId = currentUser.Id;
var today = SystemClock.Instance.GetCurrentInstant().InUtc().Date; var today = SystemClock.Instance.GetCurrentInstant().InUtc().Date;
var localTime = new TimeOnly(0, 0); var localTime = new TimeOnly(0, 0);
var startOfDay = today.ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant(); var startOfDay = today.ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant();
var endOfDay = today.PlusDays(1).ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant(); var endOfDay = today.PlusDays(1).ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant();
var result = await db.AccountCheckInResults var result = await db.AccountCheckInResults
.Where(x => x.AccountId == userId) .Where(x => x.AccountId == userId)
.Where(x => x.CreatedAt >= startOfDay && x.CreatedAt < endOfDay) .Where(x => x.CreatedAt >= startOfDay && x.CreatedAt < endOfDay)
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => x.CreatedAt)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
return result is null ? NotFound() : Ok(result); return result is null ? NotFound() : Ok(result);
} }
[HttpPost("me/check-in")] [HttpPost("me/check-in")]
[Authorize] [Authorize]
public async Task<ActionResult<CheckInResult>> DoCheckIn([FromBody] string? captchaToken) public async Task<ActionResult<CheckInResult>> DoCheckIn([FromBody] string? captchaToken)
{ {
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var isAvailable = await events.CheckInDailyIsAvailable(currentUser); var isAvailable = await events.CheckInDailyIsAvailable(currentUser);
if (!isAvailable) if (!isAvailable)
return BadRequest("Check-in is not available for today."); return BadRequest("Check-in is not available for today.");
var needsCaptcha = events.CheckInDailyDoAskCaptcha(currentUser); var needsCaptcha = events.CheckInDailyDoAskCaptcha(currentUser);
return needsCaptcha switch return needsCaptcha switch
{ {
@ -303,6 +303,23 @@ public class AccountController(
}; };
} }
[HttpGet("me/calendar")]
[Authorize]
public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month, [FromQuery] int? year)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var currentDate = SystemClock.Instance.GetCurrentInstant().InUtc().Date;
month ??= currentDate.Month;
year ??= currentDate.Year;
if (month is < 1 or > 12) return BadRequest("Invalid month.");
if (year < 1) return BadRequest("Invalid year.");
var calendar = await events.GetEventCalendar(currentUser, month.Value, year.Value);
return Ok(calendar);
}
[HttpGet("search")] [HttpGet("search")]
public async Task<List<Account>> Search([FromQuery] string query, [FromQuery] int take = 20) public async Task<List<Account>> Search([FromQuery] string query, [FromQuery] int take = 20)
{ {

View File

@ -167,4 +167,45 @@ public class AccountEventService(
return result; return result;
} }
public async Task<List<DailyEventResponse>> GetEventCalendar(Account user, int month, int year = 0)
{
if (year == 0)
year = SystemClock.Instance.GetCurrentInstant().InUtc().Date.Year;
// Create start and end dates for the specified month
var startOfMonth = new LocalDate(year, month, 1).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant();
var endOfMonth = startOfMonth.Plus(Duration.FromDays(DateTime.DaysInMonth(year, month)));
var statuses = await db.AccountStatuses
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
.OrderBy(x => x.CreatedAt)
.ToListAsync();
var checkIn = await db.AccountCheckInResults
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
.ToListAsync();
var dates = Enumerable.Range(1, DateTime.DaysInMonth(year, month))
.Select(day => new LocalDate(year, month, day).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant())
.ToList();
var statusesByDate = statuses
.GroupBy(s => s.CreatedAt.InUtc().Date)
.ToDictionary(g => g.Key, g => g.ToList());
var checkInByDate = checkIn
.ToDictionary(c => c.CreatedAt.InUtc().Date);
return dates.Select(date =>
{
var utcDate = date.InUtc().Date;
return new DailyEventResponse
{
Date = date,
CheckInResult = checkInByDate.GetValueOrDefault(utcDate),
Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<Status>())
};
}).ToList();
}
} }

View File

@ -49,4 +49,14 @@ public class FortuneTip
public bool IsPositive { get; set; } public bool IsPositive { get; set; }
public string Title { get; set; } = null!; public string Title { get; set; } = null!;
public string Content { get; set; } = null!; public string Content { get; set; } = null!;
}
/// <summary>
/// This method should not be mapped. Used to generate the daily event calendar.
/// </summary>
public class DailyEventResponse
{
public Instant Date { get; set; }
public CheckInResult? CheckInResult { get; set; }
public ICollection<Status> Statuses { get; set; } = new List<Status>();
} }