✨ Account friends overview endpoint
This commit is contained in:
@@ -478,6 +478,54 @@ public class AccountEventService(
|
|||||||
return activities;
|
return activities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<Guid, List<SnPresenceActivity>>> GetActiveActivitiesBatch(List<Guid> userIds)
|
||||||
|
{
|
||||||
|
var results = new Dictionary<Guid, List<SnPresenceActivity>>();
|
||||||
|
var cacheMissUserIds = new List<Guid>();
|
||||||
|
|
||||||
|
// Try to get activities from cache first
|
||||||
|
foreach (var userId in userIds)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{ActivityCacheKey}{userId}";
|
||||||
|
var cachedActivities = await cache.GetAsync<List<SnPresenceActivity>>(cacheKey);
|
||||||
|
if (cachedActivities != null)
|
||||||
|
{
|
||||||
|
results[userId] = cachedActivities;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cacheMissUserIds.Add(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all activities were found in cache, return early
|
||||||
|
if (cacheMissUserIds.Count == 0) return results;
|
||||||
|
|
||||||
|
// Fetch remaining activities from database in a single query
|
||||||
|
var now = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
var activitiesFromDb = await db.PresenceActivities
|
||||||
|
.Where(e => cacheMissUserIds.Contains(e.AccountId) && e.LeaseExpiresAt > now && e.DeletedAt == null)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Group activities by user ID and update cache
|
||||||
|
var activitiesByUser = activitiesFromDb
|
||||||
|
.GroupBy(a => a.AccountId)
|
||||||
|
.ToDictionary(g => g.Key, g => g.ToList());
|
||||||
|
|
||||||
|
foreach (var userId in cacheMissUserIds)
|
||||||
|
{
|
||||||
|
var userActivities = activitiesByUser.GetValueOrDefault(userId, new List<SnPresenceActivity>());
|
||||||
|
results[userId] = userActivities;
|
||||||
|
|
||||||
|
// Update cache for this user
|
||||||
|
var cacheKey = $"{ActivityCacheKey}{userId}";
|
||||||
|
await cache.SetWithGroupsAsync(cacheKey, userActivities, [$"{AccountService.AccountCachePrefix}{userId}"],
|
||||||
|
TimeSpan.FromMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<(List<SnPresenceActivity>, int)> GetAllActivities(Guid userId, int offset = 0, int take = 20)
|
public async Task<(List<SnPresenceActivity>, int)> GetAllActivities(Guid userId, int offset = 0, int take = 20)
|
||||||
{
|
{
|
||||||
var query = db.PresenceActivities
|
var query = db.PresenceActivities
|
||||||
|
|||||||
60
DysonNetwork.Pass/Account/FriendsController.cs
Normal file
60
DysonNetwork.Pass/Account/FriendsController.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using DysonNetwork.Shared.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Account;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/friends")]
|
||||||
|
public class FriendsController(AppDatabase db, RelationshipService rels, AccountEventService events) : ControllerBase
|
||||||
|
{
|
||||||
|
public class FriendOverviewItem
|
||||||
|
{
|
||||||
|
public SnAccount Account { get; set; } = null!;
|
||||||
|
public SnAccountStatus Status { get; set; } = null!;
|
||||||
|
public List<SnPresenceActivity> Activities { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("overview")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<List<FriendOverviewItem>>> GetOverview()
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var friendIds = await rels.ListAccountFriends(currentUser);
|
||||||
|
|
||||||
|
// Fetch data in parallel using batch methods for better performance
|
||||||
|
var accountsTask = db.Accounts
|
||||||
|
.Where(a => friendIds.Contains(a.Id))
|
||||||
|
.Include(a => a.Profile)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var statusesTask = events.GetStatuses(friendIds);
|
||||||
|
var activitiesTask = events.GetActiveActivitiesBatch(friendIds);
|
||||||
|
|
||||||
|
// Wait for all data to be fetched
|
||||||
|
await Task.WhenAll(accountsTask, statusesTask, activitiesTask);
|
||||||
|
|
||||||
|
var accounts = accountsTask.Result;
|
||||||
|
var statuses = statusesTask.Result;
|
||||||
|
var activities = activitiesTask.Result;
|
||||||
|
|
||||||
|
var result = new List<FriendOverviewItem>();
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
var status = statuses.GetValueOrDefault(account.Id);
|
||||||
|
var accountActivities = activities.GetValueOrDefault(account.Id, []);
|
||||||
|
|
||||||
|
result.Add(new FriendOverviewItem
|
||||||
|
{
|
||||||
|
Account = account,
|
||||||
|
Status = status ?? new SnAccountStatus { AccountId = account.Id },
|
||||||
|
Activities = accountActivities
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user