From 24836fc60630242f98b35933d99609734e0a1736 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 25 Dec 2025 21:36:26 +0800 Subject: [PATCH] :sparkles: Rewind service basis and sphere service rewind --- .../Account/Rewind/AccountRewindController.cs | 6 ++ .../Account/Rewind/AccountRewindService.cs | 6 ++ DysonNetwork.Pass/AppDatabase.cs | 2 + DysonNetwork.Shared/Models/SnRewindPoint.cs | 14 +++ DysonNetwork.Shared/Proto/rewind.proto | 19 ++++ .../Publisher/PublisherService.cs | 12 ++- .../Rewind/SphereRewindServiceGrpc.cs | 97 +++++++++++++++++++ .../Startup/ApplicationConfiguration.cs | 2 + 8 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 DysonNetwork.Pass/Account/Rewind/AccountRewindController.cs create mode 100644 DysonNetwork.Pass/Account/Rewind/AccountRewindService.cs create mode 100644 DysonNetwork.Shared/Models/SnRewindPoint.cs create mode 100644 DysonNetwork.Shared/Proto/rewind.proto create mode 100644 DysonNetwork.Sphere/Rewind/SphereRewindServiceGrpc.cs diff --git a/DysonNetwork.Pass/Account/Rewind/AccountRewindController.cs b/DysonNetwork.Pass/Account/Rewind/AccountRewindController.cs new file mode 100644 index 0000000..71fe5f8 --- /dev/null +++ b/DysonNetwork.Pass/Account/Rewind/AccountRewindController.cs @@ -0,0 +1,6 @@ +namespace DysonNetwork.Pass.Account.Rewind; + +public class AccountRewindController +{ + +} \ No newline at end of file diff --git a/DysonNetwork.Pass/Account/Rewind/AccountRewindService.cs b/DysonNetwork.Pass/Account/Rewind/AccountRewindService.cs new file mode 100644 index 0000000..1bf9954 --- /dev/null +++ b/DysonNetwork.Pass/Account/Rewind/AccountRewindService.cs @@ -0,0 +1,6 @@ +namespace DysonNetwork.Pass.Account.Rewind; + +public class AccountRewindService(AppDatabase db) +{ + +} \ No newline at end of file diff --git a/DysonNetwork.Pass/AppDatabase.cs b/DysonNetwork.Pass/AppDatabase.cs index a749516..5eccbb2 100644 --- a/DysonNetwork.Pass/AppDatabase.cs +++ b/DysonNetwork.Pass/AppDatabase.cs @@ -63,6 +63,8 @@ public class AppDatabase( public DbSet AffiliationSpells { get; set; } = null!; public DbSet AffiliationResults { get; set; } = null!; + + public DbSet RewindPoints { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/DysonNetwork.Shared/Models/SnRewindPoint.cs b/DysonNetwork.Shared/Models/SnRewindPoint.cs new file mode 100644 index 0000000..69ce03b --- /dev/null +++ b/DysonNetwork.Shared/Models/SnRewindPoint.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace DysonNetwork.Shared.Models; + +public class SnRewindPoint +{ + public Guid Id { get; set; } = Guid.NewGuid(); + public int Year { get; set; } = DateTime.UtcNow.Year; + + [Column(TypeName = "jsonb")] public Dictionary Data { get; set; } = new(); + + public Guid AccountId { get; set; } + public SnAccount Account { get; set; } = null!; +} diff --git a/DysonNetwork.Shared/Proto/rewind.proto b/DysonNetwork.Shared/Proto/rewind.proto new file mode 100644 index 0000000..fc14080 --- /dev/null +++ b/DysonNetwork.Shared/Proto/rewind.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package proto; + +option csharp_namespace = "DysonNetwork.Shared.Proto"; + +message RewindEvent { + string account_id = 1; + optional bytes data = 2; + string service_id = 3; +} + +message RequestRewindEvent { + string account_id = 1; +} + +service RewindService { + rpc GetRewindEvent(RequestRewindEvent) returns (RewindEvent) {} +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Publisher/PublisherService.cs b/DysonNetwork.Sphere/Publisher/PublisherService.cs index 1a90368..d309445 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherService.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherService.cs @@ -20,6 +20,14 @@ public class PublisherService( RemoteAccountService remoteAccounts ) { + public async Task GetPublisherLoaded(Guid id) + { + var publisher = await db.Publishers + .Where(p => p.Id == id) + .FirstOrDefaultAsync(); + return publisher is null ? null : (await LoadIndividualPublisherAccounts([publisher])).First(); + } + public async Task GetPublisherByName(string name) { return await db.Publishers @@ -453,7 +461,7 @@ public class PublisherService( public async Task> LoadIndividualPublisherAccounts(ICollection publishers) { var accountIds = publishers - .Where(p => p.AccountId.HasValue && p.Type == PublisherType.Individual) + .Where(p => p is { AccountId: not null, Type: PublisherType.Individual }) .Select(p => p.AccountId!.Value) .ToList(); if (accountIds.Count == 0) return publishers.ToList(); @@ -638,7 +646,7 @@ public class PublisherService( if (!publisherAccounts.TryGetValue(publisherId, out var receivers) || receivers.Count == 0) continue; - + var publisherName = publishers.TryGetValue(publisherId, out var pub) ? pub.Name : "unknown"; // Set social credit for receivers, expired before next settle diff --git a/DysonNetwork.Sphere/Rewind/SphereRewindServiceGrpc.cs b/DysonNetwork.Sphere/Rewind/SphereRewindServiceGrpc.cs new file mode 100644 index 0000000..7307bb0 --- /dev/null +++ b/DysonNetwork.Sphere/Rewind/SphereRewindServiceGrpc.cs @@ -0,0 +1,97 @@ +using System.Globalization; +using DysonNetwork.Shared.Proto; +using DysonNetwork.Shared.Registry; +using Grpc.Core; +using Microsoft.EntityFrameworkCore; + +namespace DysonNetwork.Sphere.Rewind; + +public class SphereRewindServiceGrpc( + AppDatabase db, + RemoteAccountService remoteAccounts, + Publisher.PublisherService ps +) : RewindService.RewindServiceBase +{ + public override async Task GetRewindEvent(RequestRewindEvent request, ServerCallContext context) + { + var accountId = Guid.Parse(request.AccountId); + + // Audience data + var mostLovedPublisherClue = + await db.PostReactions + .Where(p => p.AccountId == accountId && p.Attitude == Shared.Models.PostReactionAttitude.Positive) + .GroupBy(p => p.Post.PublisherId) + .OrderByDescending(g => g.Count()) + .Select(g => new { PublisherId = g.Key, ReactionCount = g.Count() }) + .FirstOrDefaultAsync(); + var mostLovedPublisher = mostLovedPublisherClue is not null + ? ps.GetPublisherLoaded(mostLovedPublisherClue.PublisherId) + : null; + + // Creator data + var publishers = await db.PublisherMembers + .Where(pm => pm.AccountId == accountId) + .Select(pm => pm.PublisherId) + .ToListAsync(); + + var mostLovedAudienceClue = + await db.PostReactions + .Where(pr => + pr.Attitude == Shared.Models.PostReactionAttitude.Positive && + publishers.Contains(pr.Post.PublisherId)) + .GroupBy(pr => pr.AccountId) + .OrderByDescending(g => g.Count()) + .Select(g => new { AccountId = g.Key, ReactionCount = g.Count() }) + .FirstOrDefaultAsync(); + var mostLovedAudience = mostLovedAudienceClue is not null + ? await remoteAccounts.GetAccount(mostLovedAudienceClue.AccountId) + : null; + + var posts = await db.Posts + .Where(p => publishers.Contains(p.PublisherId)) + .ToListAsync(); + var postTotalCount = posts.Count; + var mostPopularPost = posts + .OrderByDescending(p => p.Upvotes - p.Downvotes) + .FirstOrDefault(); + var mostProductiveDay = posts + .GroupBy(p => p.CreatedAt.ToDateTimeUtc().Date) + .OrderByDescending(g => g.Count()) + .Select(g => new { Date = g.Key, PostCount = g.Count() }) + .FirstOrDefault(); + + var data = new Dictionary + { + ["total_count"] = postTotalCount, + ["most_popular_post"] = mostPopularPost, + ["most_productive_day"] = mostProductiveDay is not null + ? new Dictionary + { + ["date"] = mostProductiveDay.Date.ToString(CultureInfo.InvariantCulture), + ["post_count"] = mostProductiveDay.PostCount, + } + : null, + ["most_loved_publisher"] = mostLovedPublisherClue is not null + ? new Dictionary + { + ["publisher"] = mostLovedPublisher, + ["upvote_counts"] = mostLovedPublisherClue.ReactionCount, + } + : null, + ["most_loved_audience"] = mostLovedAudienceClue is not null + ? new Dictionary + { + ["account"] = mostLovedAudience, + ["upvote_counts"] = mostLovedAudienceClue.ReactionCount, + } + : null, + }; + + return new RewindEvent + { + ServiceId = "sphere", + AccountId = request.AccountId, + Data = GrpcTypeHelper.ConvertObjectToByteString(data) + }; + } +} diff --git a/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs b/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs index a619859..c50fa44 100644 --- a/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs +++ b/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs @@ -2,6 +2,7 @@ using DysonNetwork.Shared.Auth; using DysonNetwork.Shared.Http; using DysonNetwork.Sphere.Post; using DysonNetwork.Sphere.Publisher; +using DysonNetwork.Sphere.Rewind; namespace DysonNetwork.Sphere.Startup; @@ -23,6 +24,7 @@ public static class ApplicationConfiguration // Map gRPC services app.MapGrpcService(); app.MapGrpcService(); + app.MapGrpcService(); app.MapGrpcReflectionService(); return app;