From b49cd1c382c1ed3552788dfce0295a6250aac5f8 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 17 Aug 2025 23:32:58 +0800 Subject: [PATCH] :sparkles: Realm and chat with status listing member API --- DysonNetwork.Pass/Account/Event.cs | 2 + DysonNetwork.Shared/Data/AccountStatus.cs | 55 ++++++++++++ DysonNetwork.Shared/Proto/account.proto | 1 + .../Registry/AccountClientHelper.cs | 13 ++- DysonNetwork.Sphere/Chat/ChatRoom.cs | 2 + .../Chat/ChatRoomController.cs | 89 ++++++++++--------- DysonNetwork.Sphere/Realm/Realm.cs | 1 + DysonNetwork.Sphere/Realm/RealmController.cs | 87 ++++++++++-------- .../WebReader/WebFeedService.cs | 2 +- DysonNetwork.sln.DotSettings.user | 1 + 10 files changed, 172 insertions(+), 81 deletions(-) create mode 100644 DysonNetwork.Shared/Data/AccountStatus.cs diff --git a/DysonNetwork.Pass/Account/Event.cs b/DysonNetwork.Pass/Account/Event.cs index 78708d5..ae1dfb9 100644 --- a/DysonNetwork.Pass/Account/Event.cs +++ b/DysonNetwork.Pass/Account/Event.cs @@ -45,6 +45,7 @@ public class Status : ModelBase IsNotDisturb = IsNotDisturb, Label = Label ?? string.Empty, ClearedAt = ClearedAt?.ToTimestamp(), + AccountId = AccountId.ToString() }; return proto; @@ -68,6 +69,7 @@ public class Status : ModelBase IsNotDisturb = proto.IsNotDisturb, Label = proto.Label, ClearedAt = proto.ClearedAt?.ToInstant(), + AccountId = Guid.Parse(proto.AccountId) }; return status; diff --git a/DysonNetwork.Shared/Data/AccountStatus.cs b/DysonNetwork.Shared/Data/AccountStatus.cs new file mode 100644 index 0000000..2f2f23d --- /dev/null +++ b/DysonNetwork.Shared/Data/AccountStatus.cs @@ -0,0 +1,55 @@ +using DysonNetwork.Shared.Proto; +using NodaTime; +using NodaTime.Serialization.Protobuf; + +namespace DysonNetwork.Shared.Data; + +public class AccountStatusReference : ModelBase +{ + public Guid Id { get; set; } = Guid.NewGuid(); + public StatusAttitude Attitude { get; set; } + public bool IsOnline { get; set; } + public bool IsCustomized { get; set; } = true; + public bool IsInvisible { get; set; } + public bool IsNotDisturb { get; set; } + public string? Label { get; set; } + public Instant? ClearedAt { get; set; } + + public Guid AccountId { get; set; } + + public AccountStatus ToProtoValue() + { + var proto = new AccountStatus + { + Id = Id.ToString(), + Attitude = Attitude, + IsOnline = IsOnline, + IsCustomized = IsCustomized, + IsInvisible = IsInvisible, + IsNotDisturb = IsNotDisturb, + Label = Label ?? string.Empty, + ClearedAt = ClearedAt?.ToTimestamp(), + AccountId = AccountId.ToString() + }; + + return proto; + } + + public static AccountStatusReference FromProtoValue(AccountStatus proto) + { + var status = new AccountStatusReference + { + Id = Guid.Parse(proto.Id), + Attitude = proto.Attitude, + IsOnline = proto.IsOnline, + IsCustomized = proto.IsCustomized, + IsInvisible = proto.IsInvisible, + IsNotDisturb = proto.IsNotDisturb, + Label = proto.Label, + ClearedAt = proto.ClearedAt?.ToInstant(), + AccountId = Guid.Parse(proto.AccountId) + }; + + return status; + } +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Proto/account.proto b/DysonNetwork.Shared/Proto/account.proto index 4fba760..51ddd22 100644 --- a/DysonNetwork.Shared/Proto/account.proto +++ b/DysonNetwork.Shared/Proto/account.proto @@ -52,6 +52,7 @@ message AccountStatus { bool is_not_disturb = 6; google.protobuf.StringValue label = 7; google.protobuf.Timestamp cleared_at = 8; + string account_id = 9; } // Profile contains detailed information about a user diff --git a/DysonNetwork.Shared/Registry/AccountClientHelper.cs b/DysonNetwork.Shared/Registry/AccountClientHelper.cs index 977eab7..d816add 100644 --- a/DysonNetwork.Shared/Registry/AccountClientHelper.cs +++ b/DysonNetwork.Shared/Registry/AccountClientHelper.cs @@ -1,3 +1,4 @@ +using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; namespace DysonNetwork.Shared.Registry; @@ -10,7 +11,7 @@ public class AccountClientHelper(AccountService.AccountServiceClient accounts) var response = await accounts.GetAccountAsync(request); return response; } - + public async Task> GetAccountBatch(List ids) { var request = new GetAccountBatchRequest(); @@ -18,4 +19,14 @@ public class AccountClientHelper(AccountService.AccountServiceClient accounts) var response = await accounts.GetAccountBatchAsync(request); return response.Accounts.ToList(); } + + public async Task> GetAccountStatusBatch(List ids) + { + var request = new GetAccountBatchRequest(); + request.Id.AddRange(ids.Select(id => id.ToString())); + var response = await accounts.GetAccountStatusBatchAsync(request); + return response.Statuses + .Select(AccountStatusReference.FromProtoValue) + .ToDictionary(s => s.AccountId, s => s); + } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/Chat/ChatRoom.cs b/DysonNetwork.Sphere/Chat/ChatRoom.cs index d619cba..73eb46e 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoom.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoom.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using DysonNetwork.Shared.Data; +using DysonNetwork.Shared.Proto; using NodaTime; using Account = DysonNetwork.Pass.Account.Account; @@ -75,6 +76,7 @@ public class ChatMember : ModelBase public ChatRoom ChatRoom { get; set; } = null!; public Guid AccountId { get; set; } [NotMapped] public Account? Account { get; set; } + [NotMapped] public AccountStatusReference? Status { get; set; } [MaxLength(1024)] public string? Nick { get; set; } diff --git a/DysonNetwork.Sphere/Chat/ChatRoomController.cs b/DysonNetwork.Sphere/Chat/ChatRoomController.cs index 7f2a737..1199a94 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoomController.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoomController.cs @@ -5,6 +5,7 @@ using DysonNetwork.Shared; using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; +using DysonNetwork.Shared.Registry; using DysonNetwork.Sphere.Localization; using DysonNetwork.Sphere.Realm; using Grpc.Core; @@ -25,7 +26,8 @@ public class ChatRoomController( FileService.FileServiceClient files, FileReferenceService.FileReferenceServiceClient fileRefs, ActionLogService.ActionLogServiceClient als, - PusherService.PusherServiceClient pusher + PusherService.PusherServiceClient pusher, + AccountClientHelper accountsHelper ) : ControllerBase { [HttpGet("{id:guid}")] @@ -164,7 +166,7 @@ public class ChatRoomController( public class ChatRoomRequest { - [Required][MaxLength(1024)] public string? Name { get; set; } + [Required] [MaxLength(1024)] public string? Name { get; set; } [MaxLength(4096)] public string? Description { get; set; } [MaxLength(32)] public string? PictureId { get; set; } [MaxLength(32)] public string? BackgroundId { get; set; } @@ -507,45 +509,52 @@ public class ChatRoomController( .Where(m => m.ChatRoomId == roomId) .Where(m => m.LeaveAt == null); - // if (withStatus) - // { - // var members = await query - // .OrderBy(m => m.JoinedAt) - // .ToListAsync(); - // - // var memberStatuses = await aes.GetStatuses(members.Select(m => m.AccountId).ToList()); - // - // if (!string.IsNullOrEmpty(status)) - // { - // members = members.Where(m => - // memberStatuses.TryGetValue(m.AccountId, out var s) && s.Label != null && - // s.Label.Equals(status, StringComparison.OrdinalIgnoreCase)).ToList(); - // } - // - // members = members.OrderByDescending(m => memberStatuses.TryGetValue(m.AccountId, out var s) && s.IsOnline) - // .ToList(); - // - // var total = members.Count; - // Response.Headers.Append("X-Total", total.ToString()); - // - // var result = members.Skip(skip).Take(take).ToList(); - // - // return Ok(await crs.LoadMemberAccounts(result)); - // } - // else - // { - var total = await query.CountAsync(); - Response.Headers.Append("X-Total", total.ToString()); + if (withStatus) + { + var members = await query + .OrderBy(m => m.JoinedAt) + .ToListAsync(); - var members = await query - .OrderBy(m => m.JoinedAt) - .Skip(offset) - .Take(take) - .ToListAsync(); - members = await crs.LoadMemberAccounts(members); + var memberStatuses = await accountsHelper.GetAccountStatusBatch( + members.Select(m => m.AccountId).ToList() + ); - return Ok(members.Where(m => m.Account is not null).ToList()); - // } + if (!string.IsNullOrEmpty(status)) + { + members = members + .Select(m => + { + m.Status = memberStatuses.TryGetValue(m.AccountId, out var s) ? s : null; + return m; + }) + .ToList(); + } + + members = members + .OrderByDescending(m => m.Status?.IsOnline ?? false) + .ToList(); + + var total = members.Count; + Response.Headers.Append("X-Total", total.ToString()); + + var result = members.Skip(offset).Take(take).ToList(); + + return Ok(await crs.LoadMemberAccounts(result)); + } + else + { + var total = await query.CountAsync(); + Response.Headers.Append("X-Total", total.ToString()); + + var members = await query + .OrderBy(m => m.JoinedAt) + .Skip(offset) + .Take(take) + .ToListAsync(); + members = await crs.LoadMemberAccounts(members); + + return Ok(members.Where(m => m.Account is not null).ToList()); + } } @@ -985,4 +994,4 @@ public class ChatRoomController( } ); } -} +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Realm/Realm.cs b/DysonNetwork.Sphere/Realm/Realm.cs index c6b7eb3..af8aafd 100644 --- a/DysonNetwork.Sphere/Realm/Realm.cs +++ b/DysonNetwork.Sphere/Realm/Realm.cs @@ -49,6 +49,7 @@ public class RealmMember : ModelBase public Realm Realm { get; set; } = null!; public Guid AccountId { get; set; } [NotMapped] public Account? Account { get; set; } + [NotMapped] public AccountStatusReference? Status { get; set; } public int Role { get; set; } = RealmMemberRole.Normal; public Instant? JoinedAt { get; set; } diff --git a/DysonNetwork.Sphere/Realm/RealmController.cs b/DysonNetwork.Sphere/Realm/RealmController.cs index 79ef11a..a313152 100644 --- a/DysonNetwork.Sphere/Realm/RealmController.cs +++ b/DysonNetwork.Sphere/Realm/RealmController.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; +using DysonNetwork.Shared.Registry; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -18,7 +19,8 @@ public class RealmController( FileService.FileServiceClient files, FileReferenceService.FileReferenceServiceClient fileRefs, ActionLogService.ActionLogServiceClient als, - AccountService.AccountServiceClient accounts + AccountService.AccountServiceClient accounts, + AccountClientHelper accountsHelper ) : Controller { [HttpGet("{slug}")] @@ -234,45 +236,52 @@ public class RealmController( .Where(m => m.RealmId == realm.Id) .Where(m => m.LeaveAt == null); - // if (withStatus) - // { - // var members = await query - // .OrderBy(m => m.CreatedAt) - // .ToListAsync(); - // - // var memberStatuses = await aes.GetStatuses(members.Select(m => m.AccountId).ToList()); - // - // if (!string.IsNullOrEmpty(status)) - // { - // members = members.Where(m => - // memberStatuses.TryGetValue(m.AccountId, out var s) && s.Label != null && - // s.Label.Equals(status, StringComparison.OrdinalIgnoreCase)).ToList(); - // } - // - // members = members.OrderByDescending(m => memberStatuses.TryGetValue(m.AccountId, out var s) && s.IsOnline) - // .ToList(); - // - // var total = members.Count; - // Response.Headers["X-Total"] = total.ToString(); - // - // var result = members.Skip(offset).Take(take).ToList(); - // - // return Ok(await rs.LoadMemberAccounts(result)); - // } - // else - // { - var total = await query.CountAsync(); - Response.Headers["X-Total"] = total.ToString(); + if (withStatus) + { + var members = await query + .OrderBy(m => m.JoinedAt) + .ToListAsync(); - var members = await query - .OrderBy(m => m.CreatedAt) - .Skip(offset) - .Take(take) - .ToListAsync(); - members = await rs.LoadMemberAccounts(members); + var memberStatuses = await accountsHelper.GetAccountStatusBatch( + members.Select(m => m.AccountId).ToList() + ); - return Ok(members.Where(m => m.Account is not null).ToList()); - // } + if (!string.IsNullOrEmpty(status)) + { + members = members + .Select(m => + { + m.Status = memberStatuses.TryGetValue(m.AccountId, out var s) ? s : null; + return m; + }) + .ToList(); + } + + members = members + .OrderByDescending(m => m.Status?.IsOnline ?? false) + .ToList(); + + var total = members.Count; + Response.Headers.Append("X-Total", total.ToString()); + + var result = members.Skip(offset).Take(take).ToList(); + + return Ok(await rs.LoadMemberAccounts(result)); + } + else + { + var total = await query.CountAsync(); + Response.Headers["X-Total"] = total.ToString(); + + var members = await query + .OrderBy(m => m.CreatedAt) + .Skip(offset) + .Take(take) + .ToListAsync(); + members = await rs.LoadMemberAccounts(members); + + return Ok(members.Where(m => m.Account is not null).ToList()); + } } @@ -689,7 +698,7 @@ public class RealmController( .Where(c => c.RealmId == realm.Id) .Select(c => c.Id) .ToListAsync(); - + db.Realms.Remove(realm); await db.SaveChangesAsync(); diff --git a/DysonNetwork.Sphere/WebReader/WebFeedService.cs b/DysonNetwork.Sphere/WebReader/WebFeedService.cs index 8ced368..8af8a26 100644 --- a/DysonNetwork.Sphere/WebReader/WebFeedService.cs +++ b/DysonNetwork.Sphere/WebReader/WebFeedService.cs @@ -23,7 +23,7 @@ public class WebFeedService( PublisherId = publisher.Id, }; - database.Set().Add(feed); + database.WebFeeds.Add(feed); await database.SaveChangesAsync(); return feed; diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index e70f165..0c61107 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -39,6 +39,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded