♻️ Moving to MagicOnion

This commit is contained in:
2025-07-07 21:54:51 +08:00
parent 1672d46038
commit 8d2f4a4c47
41 changed files with 790 additions and 530 deletions

View File

@ -1,7 +1,9 @@
using System.Globalization;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Services;
using MagicOnion.Server;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Localization;
using NodaTime;
@ -9,11 +11,9 @@ namespace DysonNetwork.Pass.Account;
public class AccountEventService(
AppDatabase db,
// WebSocketService ws,
// ICacheService cache,
// PaymentService payment,
ICacheService cache,
IStringLocalizer<Localization.AccountEventResource> localizer
)
) : ServiceBase<IAccountEventService>, IAccountEventService
{
private static readonly Random Random = new();
private const string StatusCacheKey = "AccountStatus_";
@ -21,18 +21,18 @@ public class AccountEventService(
public void PurgeStatusCache(Guid userId)
{
var cacheKey = $"{StatusCacheKey}{userId}";
// cache.RemoveAsync(cacheKey);
cache.RemoveAsync(cacheKey);
}
public async Task<Status> GetStatus(Guid userId)
{
var cacheKey = $"{StatusCacheKey}{userId}";
// var cachedStatus = await cache.GetAsync<Status>(cacheKey);
// if (cachedStatus is not null)
// {
// cachedStatus!.IsOnline = !cachedStatus.IsInvisible && ws.GetAccountIsConnected(userId);
// return cachedStatus;
// }
var cachedStatus = await cache.GetAsync<Status>(cacheKey);
if (cachedStatus is not null)
{
cachedStatus!.IsOnline = !cachedStatus.IsInvisible /*&& ws.GetAccountIsConnected(userId)*/;
return cachedStatus;
}
var now = SystemClock.Instance.GetCurrentInstant();
var status = await db.AccountStatuses
@ -45,8 +45,12 @@ public class AccountEventService(
if (status is not null)
{
status.IsOnline = !status.IsInvisible && isOnline;
// await cache.SetWithGroupsAsync(cacheKey, status, [$"{AccountService.AccountCachePrefix}{status.AccountId}"],
// TimeSpan.FromMinutes(5));
await cache.SetWithGroupsAsync(
cacheKey,
status,
[$"{AccountService.AccountCachePrefix}{status.AccountId}"],
TimeSpan.FromMinutes(5)
);
return status;
}
@ -62,7 +66,7 @@ public class AccountEventService(
};
}
return new Status
return new Status
{
Attitude = StatusAttitude.Neutral,
IsOnline = false,
@ -88,7 +92,7 @@ public class AccountEventService(
// }
// else
// {
cacheMissUserIds.Add(userId);
cacheMissUserIds.Add(userId);
// }
}
@ -192,27 +196,28 @@ public class AccountEventService(
return lastDate < currentDate;
}
public const string CheckInLockKey = "CheckInLock_";
private const string CheckInLockKey = "checkin-lock:";
public async Task<CheckInResult> CheckInDaily(Shared.Models.Account user)
{
var lockKey = $"{CheckInLockKey}{user.Id}";
try
{
// var lk = await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(100));
// if (lk != null)
// await lk.ReleaseAsync();
var lk = await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(100));
if (lk != null)
await lk.ReleaseAsync();
}
catch
{
// Ignore errors from this pre-check
}
// Now try to acquire the lock properly
// await using var lockObj = await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5));
// if (lockObj is null) throw new InvalidOperationException("Check-in was in progress.");
await using var lockObj =
await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5));
if (lockObj is null) throw new InvalidOperationException("Check-in was in progress.");
var cultureInfo = new CultureInfo(user.Language, false);
CultureInfo.CurrentCulture = cultureInfo;
@ -274,12 +279,53 @@ public class AccountEventService(
s.SetProperty(b => b.Experience, b => b.Experience + result.RewardExperience)
);
db.AccountCheckInResults.Add(result);
await db.SaveChangesAsync(); // Don't forget to save changes to the database
await db.SaveChangesAsync(); // Remember to save changes to the database
// The lock will be automatically released by the await using statement
return result;
}
public async Task<int> GetCheckInStreak(Shared.Models.Account user)
{
var today = SystemClock.Instance.GetCurrentInstant().InUtc().Date;
var yesterdayEnd = today.PlusDays(-1).AtMidnight().InUtc().ToInstant();
var yesterdayStart = today.PlusDays(-1).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant();
var tomorrowEnd = today.PlusDays(1).AtMidnight().InUtc().ToInstant();
var tomorrowStart = today.PlusDays(1).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant();
var yesterdayResult = await db.AccountCheckInResults
.Where(x => x.AccountId == user.Id)
.Where(x => x.CreatedAt >= yesterdayStart)
.Where(x => x.CreatedAt < yesterdayEnd)
.FirstOrDefaultAsync();
var tomorrowResult = await db.AccountCheckInResults
.Where(x => x.AccountId == user.Id)
.Where(x => x.CreatedAt >= tomorrowStart)
.Where(x => x.CreatedAt < tomorrowEnd)
.FirstOrDefaultAsync();
if (yesterdayResult is null && tomorrowResult is null)
return 1;
var results = await db.AccountCheckInResults
.Where(x => x.AccountId == user.Id)
.OrderByDescending(x => x.CreatedAt)
.ToListAsync();
var streak = 0;
var day = today;
while (results.Any(x =>
x.CreatedAt >= day.AtStartOfDayInZone(DateTimeZone.Utc).ToInstant() &&
x.CreatedAt < day.AtMidnight().InUtc().ToInstant()))
{
streak++;
day = day.PlusDays(-1);
}
return streak;
}
public async Task<List<DailyEventResponse>> GetEventCalendar(Shared.Models.Account user, int month, int year = 0,
bool replaceInvisible = false)
{