Realm and chat with status listing member API

This commit is contained in:
2025-08-17 23:32:58 +08:00
parent aa9ae5c11e
commit b49cd1c382
10 changed files with 172 additions and 81 deletions

View File

@@ -45,6 +45,7 @@ public class Status : ModelBase
IsNotDisturb = IsNotDisturb, IsNotDisturb = IsNotDisturb,
Label = Label ?? string.Empty, Label = Label ?? string.Empty,
ClearedAt = ClearedAt?.ToTimestamp(), ClearedAt = ClearedAt?.ToTimestamp(),
AccountId = AccountId.ToString()
}; };
return proto; return proto;
@@ -68,6 +69,7 @@ public class Status : ModelBase
IsNotDisturb = proto.IsNotDisturb, IsNotDisturb = proto.IsNotDisturb,
Label = proto.Label, Label = proto.Label,
ClearedAt = proto.ClearedAt?.ToInstant(), ClearedAt = proto.ClearedAt?.ToInstant(),
AccountId = Guid.Parse(proto.AccountId)
}; };
return status; return status;

View File

@@ -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;
}
}

View File

@@ -52,6 +52,7 @@ message AccountStatus {
bool is_not_disturb = 6; bool is_not_disturb = 6;
google.protobuf.StringValue label = 7; google.protobuf.StringValue label = 7;
google.protobuf.Timestamp cleared_at = 8; google.protobuf.Timestamp cleared_at = 8;
string account_id = 9;
} }
// Profile contains detailed information about a user // Profile contains detailed information about a user

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
namespace DysonNetwork.Shared.Registry; namespace DysonNetwork.Shared.Registry;
@@ -18,4 +19,14 @@ public class AccountClientHelper(AccountService.AccountServiceClient accounts)
var response = await accounts.GetAccountBatchAsync(request); var response = await accounts.GetAccountBatchAsync(request);
return response.Accounts.ToList(); return response.Accounts.ToList();
} }
public async Task<Dictionary<Guid, AccountStatusReference>> GetAccountStatusBatch(List<Guid> 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);
}
} }

View File

@@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto;
using NodaTime; using NodaTime;
using Account = DysonNetwork.Pass.Account.Account; using Account = DysonNetwork.Pass.Account.Account;
@@ -75,6 +76,7 @@ public class ChatMember : ModelBase
public ChatRoom ChatRoom { get; set; } = null!; public ChatRoom ChatRoom { get; set; } = null!;
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
[NotMapped] public Account? Account { get; set; } [NotMapped] public Account? Account { get; set; }
[NotMapped] public AccountStatusReference? Status { get; set; }
[MaxLength(1024)] public string? Nick { get; set; } [MaxLength(1024)] public string? Nick { get; set; }

View File

@@ -5,6 +5,7 @@ using DysonNetwork.Shared;
using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry;
using DysonNetwork.Sphere.Localization; using DysonNetwork.Sphere.Localization;
using DysonNetwork.Sphere.Realm; using DysonNetwork.Sphere.Realm;
using Grpc.Core; using Grpc.Core;
@@ -25,7 +26,8 @@ public class ChatRoomController(
FileService.FileServiceClient files, FileService.FileServiceClient files,
FileReferenceService.FileReferenceServiceClient fileRefs, FileReferenceService.FileReferenceServiceClient fileRefs,
ActionLogService.ActionLogServiceClient als, ActionLogService.ActionLogServiceClient als,
PusherService.PusherServiceClient pusher PusherService.PusherServiceClient pusher,
AccountClientHelper accountsHelper
) : ControllerBase ) : ControllerBase
{ {
[HttpGet("{id:guid}")] [HttpGet("{id:guid}")]
@@ -164,7 +166,7 @@ public class ChatRoomController(
public class ChatRoomRequest 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(4096)] public string? Description { get; set; }
[MaxLength(32)] public string? PictureId { get; set; } [MaxLength(32)] public string? PictureId { get; set; }
[MaxLength(32)] public string? BackgroundId { get; set; } [MaxLength(32)] public string? BackgroundId { get; set; }
@@ -507,33 +509,40 @@ public class ChatRoomController(
.Where(m => m.ChatRoomId == roomId) .Where(m => m.ChatRoomId == roomId)
.Where(m => m.LeaveAt == null); .Where(m => m.LeaveAt == null);
// if (withStatus) if (withStatus)
// { {
// var members = await query var members = await query
// .OrderBy(m => m.JoinedAt) .OrderBy(m => m.JoinedAt)
// .ToListAsync(); .ToListAsync();
//
// var memberStatuses = await aes.GetStatuses(members.Select(m => m.AccountId).ToList()); var memberStatuses = await accountsHelper.GetAccountStatusBatch(
// members.Select(m => m.AccountId).ToList()
// if (!string.IsNullOrEmpty(status)) );
// {
// members = members.Where(m => if (!string.IsNullOrEmpty(status))
// memberStatuses.TryGetValue(m.AccountId, out var s) && s.Label != null && {
// s.Label.Equals(status, StringComparison.OrdinalIgnoreCase)).ToList(); members = members
// } .Select(m =>
// {
// members = members.OrderByDescending(m => memberStatuses.TryGetValue(m.AccountId, out var s) && s.IsOnline) m.Status = memberStatuses.TryGetValue(m.AccountId, out var s) ? s : null;
// .ToList(); return m;
// })
// var total = members.Count; .ToList();
// Response.Headers.Append("X-Total", total.ToString()); }
//
// var result = members.Skip(skip).Take(take).ToList(); members = members
// .OrderByDescending(m => m.Status?.IsOnline ?? false)
// return Ok(await crs.LoadMemberAccounts(result)); .ToList();
// }
// else 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(); var total = await query.CountAsync();
Response.Headers.Append("X-Total", total.ToString()); Response.Headers.Append("X-Total", total.ToString());
@@ -545,7 +554,7 @@ public class ChatRoomController(
members = await crs.LoadMemberAccounts(members); members = await crs.LoadMemberAccounts(members);
return Ok(members.Where(m => m.Account is not null).ToList()); return Ok(members.Where(m => m.Account is not null).ToList());
// } }
} }

View File

@@ -49,6 +49,7 @@ public class RealmMember : ModelBase
public Realm Realm { get; set; } = null!; public Realm Realm { get; set; } = null!;
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
[NotMapped] public Account? Account { get; set; } [NotMapped] public Account? Account { get; set; }
[NotMapped] public AccountStatusReference? Status { get; set; }
public int Role { get; set; } = RealmMemberRole.Normal; public int Role { get; set; } = RealmMemberRole.Normal;
public Instant? JoinedAt { get; set; } public Instant? JoinedAt { get; set; }

View File

@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -18,7 +19,8 @@ public class RealmController(
FileService.FileServiceClient files, FileService.FileServiceClient files,
FileReferenceService.FileReferenceServiceClient fileRefs, FileReferenceService.FileReferenceServiceClient fileRefs,
ActionLogService.ActionLogServiceClient als, ActionLogService.ActionLogServiceClient als,
AccountService.AccountServiceClient accounts AccountService.AccountServiceClient accounts,
AccountClientHelper accountsHelper
) : Controller ) : Controller
{ {
[HttpGet("{slug}")] [HttpGet("{slug}")]
@@ -234,33 +236,40 @@ public class RealmController(
.Where(m => m.RealmId == realm.Id) .Where(m => m.RealmId == realm.Id)
.Where(m => m.LeaveAt == null); .Where(m => m.LeaveAt == null);
// if (withStatus) if (withStatus)
// { {
// var members = await query var members = await query
// .OrderBy(m => m.CreatedAt) .OrderBy(m => m.JoinedAt)
// .ToListAsync(); .ToListAsync();
//
// var memberStatuses = await aes.GetStatuses(members.Select(m => m.AccountId).ToList()); var memberStatuses = await accountsHelper.GetAccountStatusBatch(
// members.Select(m => m.AccountId).ToList()
// if (!string.IsNullOrEmpty(status)) );
// {
// members = members.Where(m => if (!string.IsNullOrEmpty(status))
// memberStatuses.TryGetValue(m.AccountId, out var s) && s.Label != null && {
// s.Label.Equals(status, StringComparison.OrdinalIgnoreCase)).ToList(); members = members
// } .Select(m =>
// {
// members = members.OrderByDescending(m => memberStatuses.TryGetValue(m.AccountId, out var s) && s.IsOnline) m.Status = memberStatuses.TryGetValue(m.AccountId, out var s) ? s : null;
// .ToList(); return m;
// })
// var total = members.Count; .ToList();
// Response.Headers["X-Total"] = total.ToString(); }
//
// var result = members.Skip(offset).Take(take).ToList(); members = members
// .OrderByDescending(m => m.Status?.IsOnline ?? false)
// return Ok(await rs.LoadMemberAccounts(result)); .ToList();
// }
// else 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(); var total = await query.CountAsync();
Response.Headers["X-Total"] = total.ToString(); Response.Headers["X-Total"] = total.ToString();
@@ -272,7 +281,7 @@ public class RealmController(
members = await rs.LoadMemberAccounts(members); members = await rs.LoadMemberAccounts(members);
return Ok(members.Where(m => m.Account is not null).ToList()); return Ok(members.Where(m => m.Account is not null).ToList());
// } }
} }

View File

@@ -23,7 +23,7 @@ public class WebFeedService(
PublisherId = publisher.Id, PublisherId = publisher.Id,
}; };
database.Set<WebFeed>().Add(feed); database.WebFeeds.Add(feed);
await database.SaveChangesAsync(); await database.SaveChangesAsync();
return feed; return feed;

View File

@@ -39,6 +39,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe096e6f12c5d6b49356bc34ff1ea08738f910c0929c9d717c9cba7f44288_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe096e6f12c5d6b49356bc34ff1ea08738f910c0929c9d717c9cba7f44288_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F363c1765261146f1a68840a2d3ce7e39291438_003F2a_003F960244de_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F363c1765261146f1a68840a2d3ce7e39291438_003F2a_003F960244de_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F363c1765261146f1a68840a2d3ce7e39291438_003F2a_003F960244de_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fadcd336f9cde4e71998a851d7eb945bb87e00_003F0c_003F96dc130e_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fadcd336f9cde4e71998a851d7eb945bb87e00_003F0c_003F96dc130e_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>