From aa9ae5c11e2010912eaec2a702e5278a6c81d9cd Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 17 Aug 2025 22:30:11 +0800 Subject: [PATCH] :sparkles: Account status GRPC API --- .../Account/AccountServiceGrpc.cs | 21 +++++++ DysonNetwork.Pass/Account/Event.cs | 55 +++++++++++++++++-- DysonNetwork.Shared/Proto/account.proto | 47 ++++++++++++---- 3 files changed, 109 insertions(+), 14 deletions(-) diff --git a/DysonNetwork.Pass/Account/AccountServiceGrpc.cs b/DysonNetwork.Pass/Account/AccountServiceGrpc.cs index 8abda2e..0daa9de 100644 --- a/DysonNetwork.Pass/Account/AccountServiceGrpc.cs +++ b/DysonNetwork.Pass/Account/AccountServiceGrpc.cs @@ -9,6 +9,7 @@ namespace DysonNetwork.Pass.Account; public class AccountServiceGrpc( AppDatabase db, + AccountEventService accountEvents, RelationshipService relationships, SubscriptionService subscriptions, IClock clock, @@ -68,6 +69,26 @@ public class AccountServiceGrpc( return response; } + public override async Task GetAccountStatus(GetAccountRequest request, ServerCallContext context) + { + var accountId = Guid.Parse(request.Id); + var status = await accountEvents.GetStatus(accountId); + return status.ToProtoValue(); + } + + public override async Task GetAccountStatusBatch(GetAccountBatchRequest request, ServerCallContext context) + { + var accountIds = request.Id + .Select(id => Guid.TryParse(id, out var accountId) ? accountId : (Guid?)null) + .Where(id => id.HasValue) + .Select(id => id!.Value) + .ToList(); + var statuses = await accountEvents.GetStatuses(accountIds); + var response = new GetAccountStatusBatchResponse(); + response.Statuses.AddRange(statuses.Select(s => s.Value.ToProtoValue())); + return response; + } + public override async Task LookupAccountBatch(LookupAccountBatchRequest request, ServerCallContext context) { diff --git a/DysonNetwork.Pass/Account/Event.cs b/DysonNetwork.Pass/Account/Event.cs index 9cbc49b..78708d5 100644 --- a/DysonNetwork.Pass/Account/Event.cs +++ b/DysonNetwork.Pass/Account/Event.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using DysonNetwork.Shared.Data; using NodaTime; +using NodaTime.Serialization.Protobuf; namespace DysonNetwork.Pass.Account; @@ -22,9 +23,55 @@ public class Status : ModelBase public bool IsNotDisturb { get; set; } [MaxLength(1024)] public string? Label { get; set; } public Instant? ClearedAt { get; set; } - + public Guid AccountId { get; set; } public Account Account { get; set; } = null!; + + public Shared.Proto.AccountStatus ToProtoValue() + { + var proto = new Shared.Proto.AccountStatus + { + Id = Id.ToString(), + Attitude = Attitude switch + { + StatusAttitude.Positive => Shared.Proto.StatusAttitude.Positive, + StatusAttitude.Negative => Shared.Proto.StatusAttitude.Negative, + StatusAttitude.Neutral => Shared.Proto.StatusAttitude.Neutral, + _ => Shared.Proto.StatusAttitude.Unspecified + }, + IsOnline = IsOnline, + IsCustomized = IsCustomized, + IsInvisible = IsInvisible, + IsNotDisturb = IsNotDisturb, + Label = Label ?? string.Empty, + ClearedAt = ClearedAt?.ToTimestamp(), + }; + + return proto; + } + + public static Status FromProtoValue(Shared.Proto.AccountStatus proto) + { + var status = new Status + { + Id = Guid.Parse(proto.Id), + Attitude = proto.Attitude switch + { + Shared.Proto.StatusAttitude.Positive => StatusAttitude.Positive, + Shared.Proto.StatusAttitude.Negative => StatusAttitude.Negative, + Shared.Proto.StatusAttitude.Neutral => StatusAttitude.Neutral, + _ => StatusAttitude.Neutral + }, + IsOnline = proto.IsOnline, + IsCustomized = proto.IsCustomized, + IsInvisible = proto.IsInvisible, + IsNotDisturb = proto.IsNotDisturb, + Label = proto.Label, + ClearedAt = proto.ClearedAt?.ToInstant(), + }; + + return status; + } } public enum CheckInResultLevel @@ -43,10 +90,10 @@ public class CheckInResult : ModelBase public decimal? RewardPoints { get; set; } public int? RewardExperience { get; set; } [Column(TypeName = "jsonb")] public ICollection Tips { get; set; } = new List(); - + public Guid AccountId { get; set; } public Account Account { get; set; } = null!; - + public Instant? BackdatedFrom { get; set; } } @@ -65,4 +112,4 @@ public class DailyEventResponse public Instant Date { get; set; } public CheckInResult? CheckInResult { get; set; } public ICollection Statuses { get; set; } = new List(); -} \ No newline at end of file +} diff --git a/DysonNetwork.Shared/Proto/account.proto b/DysonNetwork.Shared/Proto/account.proto index 6ec4add..4fba760 100644 --- a/DysonNetwork.Shared/Proto/account.proto +++ b/DysonNetwork.Shared/Proto/account.proto @@ -29,11 +29,31 @@ message Account { repeated AccountConnection connections = 11; repeated Relationship outgoing_relationships = 12; repeated Relationship incoming_relationships = 13; - + google.protobuf.Timestamp created_at = 14; google.protobuf.Timestamp updated_at = 15; } +// Enum for status attitude +enum StatusAttitude { + STATUS_ATTITUDE_UNSPECIFIED = 0; + POSITIVE = 1; + NEGATIVE = 2; + NEUTRAL = 3; +} + +// AccountStatus represents the status of an account +message AccountStatus { + string id = 1; + StatusAttitude attitude = 2; + bool is_online = 3; + bool is_customized = 4; + bool is_invisible = 5; + bool is_not_disturb = 6; + google.protobuf.StringValue label = 7; + google.protobuf.Timestamp cleared_at = 8; +} + // Profile contains detailed information about a user message AccountProfile { string id = 1; @@ -59,7 +79,7 @@ message AccountProfile { CloudFile background = 20; string account_id = 21; - + google.protobuf.Timestamp created_at = 22; google.protobuf.Timestamp updated_at = 23; } @@ -72,7 +92,7 @@ message AccountContact { bool is_primary = 4; string content = 5; string account_id = 6; - + google.protobuf.Timestamp created_at = 7; google.protobuf.Timestamp updated_at = 8; } @@ -96,7 +116,7 @@ message AccountAuthFactor { google.protobuf.Timestamp expired_at = 7; string account_id = 8; map created_response = 9; // For initial setup - + google.protobuf.Timestamp created_at = 10; google.protobuf.Timestamp updated_at = 11; } @@ -121,7 +141,7 @@ message AccountBadge { google.protobuf.Timestamp activated_at = 6; // When the badge was activated google.protobuf.Timestamp expired_at = 7; // Optional expiration time string account_id = 8; // ID of the account this badge belongs to - + google.protobuf.Timestamp created_at = 9; google.protobuf.Timestamp updated_at = 10; } @@ -136,7 +156,7 @@ message AccountConnection { google.protobuf.StringValue refresh_token = 6; // Omitted from JSON serialization google.protobuf.Timestamp last_used_at = 7; string account_id = 8; - + google.protobuf.Timestamp created_at = 9; google.protobuf.Timestamp updated_at = 10; } @@ -147,7 +167,7 @@ message VerificationMark { string title = 2; string description = 3; string verified_by = 4; - + google.protobuf.Timestamp created_at = 5; google.protobuf.Timestamp updated_at = 6; } @@ -182,7 +202,7 @@ message Relationship { optional Account account = 3; optional Account related = 4; int32 status = 5; - + google.protobuf.Timestamp created_at = 6; google.protobuf.Timestamp updated_at = 7; } @@ -207,10 +227,14 @@ message ActionLog { google.protobuf.StringValue location = 6; // Geographic location of the client, derived from IP string account_id = 7; // The account that performed the action google.protobuf.StringValue session_id = 8; // The session in which the action was performed - + google.protobuf.Timestamp created_at = 9; // When the action log was created } +message GetAccountStatusBatchResponse { + repeated AccountStatus statuses = 1; +} + // ==================================== // Service Definitions // ==================================== @@ -223,6 +247,9 @@ service AccountService { rpc LookupAccountBatch(LookupAccountBatchRequest) returns (GetAccountBatchResponse) {} rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse) {} + rpc GetAccountStatus(GetAccountRequest) returns (AccountStatus) {} + rpc GetAccountStatusBatch(GetAccountBatchRequest) returns (GetAccountStatusBatchResponse) {} + // Profile Operations rpc GetProfile(GetProfileRequest) returns (AccountProfile) {} @@ -431,4 +458,4 @@ message ListRelationshipSimpleRequest { message ListRelationshipSimpleResponse { repeated string accounts_id = 1; -} \ No newline at end of file +}