🐛 Fix bugs

This commit is contained in:
LittleSheep 2025-05-10 14:10:21 +08:00
parent 4fd8a588fa
commit 790dcafeb0
4 changed files with 135 additions and 46 deletions

View File

@ -224,6 +224,44 @@ public class AccountController(
return Ok(status);
}
[HttpGet("{name}/statuses")]
public async Task<ActionResult<Status>> GetOtherStatus(string name)
{
var account = await db.Accounts.FirstOrDefaultAsync(a => a.Name == name);
if (account is null) return BadRequest();
var status = await events.GetStatus(account.Id);
status.IsInvisible = false; // Keep the invisible field not available for other users
return Ok(status);
}
[HttpPatch("me/statuses")]
[Authorize]
[RequiredPermission("global", "accounts.statuses.update")]
public async Task<ActionResult<Status>> UpdateStatus([FromBody] StatusRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
var now = SystemClock.Instance.GetCurrentInstant();
var status = await db.AccountStatuses
.Where(e => e.AccountId == currentUser.Id)
.Where(e => e.ClearedAt == null || e.ClearedAt > now)
.OrderByDescending(e => e.CreatedAt)
.FirstOrDefaultAsync();
if (status is null) return NotFound();
status.Attitude = request.Attitude;
status.IsInvisible = request.IsInvisible;
status.IsNotDisturb = request.IsNotDisturb;
status.Label = request.Label;
status.ClearedAt = request.ClearedAt;
db.Update(status);
await db.SaveChangesAsync();
events.PurgeStatusCache(currentUser.Id);
return status;
}
[HttpPost("me/statuses")]
[Authorize]
[RequiredPermission("global", "accounts.statuses.create")]
@ -305,7 +343,8 @@ public class AccountController(
[HttpGet("me/calendar")]
[Authorize]
public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month, [FromQuery] int? year)
public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month,
[FromQuery] int? year)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
@ -320,6 +359,27 @@ public class AccountController(
return Ok(calendar);
}
[HttpGet("{name}/calendar")]
public async Task<ActionResult<List<DailyEventResponse>>> GetOtherEventCalendar(
string name,
[FromQuery] int? month,
[FromQuery] int? year
)
{
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 account = await db.Accounts.FirstOrDefaultAsync(a => a.Name == name);
if (account is null) return BadRequest();
var calendar = await events.GetEventCalendar(account, month.Value, year.Value, replaceInvisible: true);
return Ok(calendar);
}
[HttpGet("search")]
public async Task<List<Account>> Search([FromQuery] string query, [FromQuery] int take = 20)
{

View File

@ -1,12 +1,10 @@
using System.Globalization;
using DysonNetwork.Sphere.Activity;
using DysonNetwork.Sphere.Connection;
using DysonNetwork.Sphere.Resources;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Localization;
using NodaTime;
using NodaTime;
namespace DysonNetwork.Sphere.Account;
@ -21,11 +19,20 @@ public class AccountEventService(
private static readonly Random Random = new();
private const string StatusCacheKey = "account_status_";
public void PurgeStatusCache(long userId)
{
var cacheKey = $"{StatusCacheKey}{userId}";
cache.Remove(cacheKey);
}
public async Task<Status> GetStatus(long userId)
{
var cacheKey = $"{StatusCacheKey}{userId}";
if (cache.TryGetValue(cacheKey, out Status? cachedStatus))
return cachedStatus!;
{
cachedStatus!.IsOnline = !cachedStatus.IsInvisible && ws.GetAccountIsConnected(userId);
return cachedStatus;
}
var now = SystemClock.Instance.GetCurrentInstant();
var status = await db.AccountStatuses
@ -33,19 +40,21 @@ public class AccountEventService(
.Where(e => e.ClearedAt == null || e.ClearedAt > now)
.OrderByDescending(e => e.CreatedAt)
.FirstOrDefaultAsync();
var isOnline = ws.GetAccountIsConnected(userId);
if (status is not null)
{
status.IsOnline = !status.IsInvisible && isOnline;
cache.Set(cacheKey, status, TimeSpan.FromMinutes(5));
return status;
}
var isOnline = ws.GetAccountIsConnected(userId);
if (isOnline)
{
return new Status
{
Attitude = StatusAttitude.Neutral,
IsOnline = true,
IsCustomized = false,
Label = "Online",
AccountId = userId,
};
@ -55,6 +64,7 @@ public class AccountEventService(
{
Attitude = StatusAttitude.Neutral,
IsOnline = false,
IsCustomized = false,
Label = "Offline",
AccountId = userId,
};
@ -85,6 +95,7 @@ public class AccountEventService(
status.ClearedAt = SystemClock.Instance.GetCurrentInstant();
db.Update(status);
await db.SaveChangesAsync();
PurgeStatusCache(user.Id);
}
private const int FortuneTipCount = 7; // This will be the max index for each type (positive/negative)
@ -168,44 +179,60 @@ public class AccountEventService(
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 =>
public async Task<List<DailyEventResponse>> GetEventCalendar(Account user, int month, int year = 0,
bool replaceInvisible = false)
{
var utcDate = date.InUtc().Date;
return new DailyEventResponse
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
.AsNoTracking()
.TagWith("GetEventCalendar_Statuses")
.Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth)
.Select(x => new Status
{
Id = x.Id,
Attitude = x.Attitude,
IsInvisible = !replaceInvisible && x.IsInvisible,
IsNotDisturb = x.IsNotDisturb,
Label = x.Label,
ClearedAt = x.ClearedAt,
AccountId = x.AccountId,
CreatedAt = x.CreatedAt
})
.OrderBy(x => x.CreatedAt)
.ToListAsync();
var checkIn = await db.AccountCheckInResults
.AsNoTracking()
.TagWith("GetEventCalendar_CheckIn")
.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 =>
{
Date = date,
CheckInResult = checkInByDate.GetValueOrDefault(utcDate),
Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<Status>())
};
}).ToList();
}
var utcDate = date.InUtc().Date;
return new DailyEventResponse
{
Date = date,
CheckInResult = checkInByDate.GetValueOrDefault(utcDate),
Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<Status>())
};
}).ToList();
}
}

View File

@ -16,6 +16,7 @@ public class Status : ModelBase
public Guid Id { get; set; } = Guid.NewGuid();
public StatusAttitude Attitude { get; set; }
[NotMapped] public bool IsOnline { get; set; }
[NotMapped] public bool IsCustomized { get; set; } = true;
public bool IsInvisible { get; set; }
public bool IsNotDisturb { get; set; }
[MaxLength(1024)] public string? Label { get; set; }

View File

@ -90,7 +90,8 @@ public class AppDatabase(
PermissionService.NewPermissionNode("group:default", "global", "chat.create", true),
PermissionService.NewPermissionNode("group:default", "global", "chat.messages.create", true),
PermissionService.NewPermissionNode("group:default", "global", "chat.realtime.create", true),
PermissionService.NewPermissionNode("group:default", "global", "accounts.statuses.create", true)
PermissionService.NewPermissionNode("group:default", "global", "accounts.statuses.create", true),
PermissionService.NewPermissionNode("group:default", "global", "accounts.statuses.update", true)
}
});
await context.SaveChangesAsync(cancellationToken);