✨ Back dated check in
This commit is contained in:
@@ -38,7 +38,7 @@ public class AccountCurrentController(
|
||||
.Include(e => e.Profile)
|
||||
.Where(e => e.Id == userId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
|
||||
var perk = await subscriptions.GetPerkSubscriptionAsync(account!.Id);
|
||||
account.PerkSubscription = perk?.ToReference();
|
||||
|
||||
@@ -120,6 +120,7 @@ public class AccountCurrentController(
|
||||
);
|
||||
profile.Picture = CloudFileReferenceObject.FromProtoValue(file);
|
||||
}
|
||||
|
||||
if (request.BackgroundId is not null)
|
||||
{
|
||||
var file = await files.GetFileAsync(new GetFileRequest { Id = request.BackgroundId });
|
||||
@@ -255,13 +256,27 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[HttpPost("check-in")]
|
||||
public async Task<ActionResult<CheckInResult>> DoCheckIn([FromBody] string? captchaToken)
|
||||
public async Task<ActionResult<CheckInResult>> DoCheckIn(
|
||||
[FromBody] string? captchaToken,
|
||||
[FromQuery] Instant? backdated = null
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
var isAvailable = await events.CheckInDailyIsAvailable(currentUser);
|
||||
if (!isAvailable)
|
||||
return BadRequest("Check-in is not available for today.");
|
||||
if (backdated is null)
|
||||
{
|
||||
var isAvailable = await events.CheckInDailyIsAvailable(currentUser);
|
||||
if (!isAvailable)
|
||||
return BadRequest("Check-in is not available for today.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentUser.PerkSubscription is null)
|
||||
return StatusCode(403, "You need to have a subscription to check-in backdated.");
|
||||
var isAvailable = await events.CheckInBackdatedIsAvailable(currentUser, backdated.Value);
|
||||
if (!isAvailable)
|
||||
return BadRequest("Check-in is not available for this date.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -269,9 +284,10 @@ public class AccountCurrentController(
|
||||
return needsCaptcha switch
|
||||
{
|
||||
true when string.IsNullOrWhiteSpace(captchaToken) => StatusCode(423,
|
||||
"Captcha is required for this check-in."),
|
||||
"Captcha is required for this check-in."
|
||||
),
|
||||
true when !await auth.ValidateCaptcha(captchaToken!) => BadRequest("Invalid captcha token."),
|
||||
_ => await events.CheckInDaily(currentUser)
|
||||
_ => await events.CheckInDaily(currentUser, backdated)
|
||||
};
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
|
@@ -5,6 +5,7 @@ using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
using NodaTime.Extensions;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
@@ -166,7 +167,7 @@ public class AccountEventService(
|
||||
}
|
||||
|
||||
private const int FortuneTipCount = 14; // This will be the max index for each type (positive/negative)
|
||||
private const string CaptchaCacheKey = "CheckInCaptcha_";
|
||||
private const string CaptchaCacheKey = "checkin:captcha:";
|
||||
private const int CaptchaProbabilityPercent = 20;
|
||||
|
||||
public async Task<bool> CheckInDailyDoAskCaptcha(Account user)
|
||||
@@ -198,9 +199,55 @@ public class AccountEventService(
|
||||
return lastDate < currentDate;
|
||||
}
|
||||
|
||||
public const string CheckInLockKey = "CheckInLock_";
|
||||
public async Task<bool> CheckInBackdatedIsAvailable(Account user, Instant backdated)
|
||||
{
|
||||
var aDay = Duration.FromDays(1);
|
||||
var backdatedStart = backdated.ToDateTimeUtc().Date.ToInstant();
|
||||
var backdatedEnd = backdated.Plus(aDay).ToDateTimeUtc().Date.ToInstant();
|
||||
|
||||
public async Task<CheckInResult> CheckInDaily(Account user)
|
||||
var backdatedDate = backdated.ToDateTimeUtc();
|
||||
var backdatedMonthStart = new DateTime(
|
||||
backdatedDate.Year,
|
||||
backdatedDate.Month,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
).ToInstant();
|
||||
var backdatedMonthEnd =
|
||||
new DateTime(
|
||||
backdatedDate.Year,
|
||||
backdatedDate.Month,
|
||||
DateTime.DaysInMonth(
|
||||
backdatedDate.Year,
|
||||
backdatedDate.Month
|
||||
),
|
||||
23,
|
||||
59,
|
||||
59
|
||||
).ToInstant();
|
||||
|
||||
// The first check, if that day already has a check-in
|
||||
var lastCheckIn = await db.AccountCheckInResults
|
||||
.Where(x => x.AccountId == user.Id)
|
||||
.Where(x => x.CreatedAt >= backdatedStart && x.CreatedAt < backdatedEnd)
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
if (lastCheckIn is not null) return false;
|
||||
|
||||
// The second check, is the user reached the max backdated check-ins limit,
|
||||
// which is once a week, which is 4 times a month
|
||||
var backdatedCheckInMonths = await db.AccountCheckInResults
|
||||
.Where(x => x.AccountId == user.Id)
|
||||
.Where(x => x.CreatedAt >= backdatedMonthStart && x.CreatedAt < backdatedMonthEnd)
|
||||
.Where(x => x.BackdatedFrom != null)
|
||||
.CountAsync();
|
||||
return backdatedCheckInMonths < 4;
|
||||
}
|
||||
|
||||
public const string CheckInLockKey = "checkin:lock:";
|
||||
|
||||
public async Task<CheckInResult> CheckInDaily(Account user, Instant? backdated = null)
|
||||
{
|
||||
var lockKey = $"{CheckInLockKey}{user.Id}";
|
||||
|
||||
@@ -254,7 +301,9 @@ public class AccountEventService(
|
||||
Level = (CheckInResultLevel)Random.Next(Enum.GetValues<CheckInResultLevel>().Length),
|
||||
AccountId = user.Id,
|
||||
RewardExperience = 100,
|
||||
RewardPoints = 10,
|
||||
RewardPoints = backdated.HasValue ? null : 10,
|
||||
BackdatedFrom = backdated.HasValue ? SystemClock.Instance.GetCurrentInstant() : null,
|
||||
CreatedAt = backdated ?? SystemClock.Instance.GetCurrentInstant(),
|
||||
};
|
||||
|
||||
var now = SystemClock.Instance.GetCurrentInstant().InUtc().Date;
|
||||
@@ -298,7 +347,7 @@ public class AccountEventService(
|
||||
|
||||
var statuses = await db.AccountStatuses
|
||||
.AsNoTracking()
|
||||
.TagWith("GetEventCalendar_Statuses")
|
||||
.TagWith("eventcal:statuses")
|
||||
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
|
||||
.Select(x => new Status
|
||||
{
|
||||
@@ -316,7 +365,7 @@ public class AccountEventService(
|
||||
|
||||
var checkIn = await db.AccountCheckInResults
|
||||
.AsNoTracking()
|
||||
.TagWith("GetEventCalendar_CheckIn")
|
||||
.TagWith("eventcal:checkin")
|
||||
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
|
||||
.ToListAsync();
|
||||
|
||||
|
@@ -46,6 +46,8 @@ public class CheckInResult : ModelBase
|
||||
|
||||
public Guid AccountId { get; set; }
|
||||
public Account Account { get; set; } = null!;
|
||||
|
||||
public Instant? BackdatedFrom { get; set; }
|
||||
}
|
||||
|
||||
public class FortuneTip
|
||||
|
Reference in New Issue
Block a user