From 57a75fe9e65a24cebccf4aeff31716b1239f21df Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 21 Aug 2025 02:28:39 +0800 Subject: [PATCH] :sparkles: Done with social credits --- .../Account/AccountController.cs | 23 ++++- .../Credit/SocialCreditRecord.cs | 18 ++++ .../Credit/SocialCreditServiceGrpc.cs | 27 ++++++ .../Leveling/ExperienceRecord.cs | 19 +++++ .../Leveling/ExperienceServiceGrpc.cs | 19 +++++ .../Startup/ApplicationConfiguration.cs | 4 + DysonNetwork.Shared/Proto/leveling.proto | 83 +++++++++++++++++++ 7 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 DysonNetwork.Pass/Credit/SocialCreditServiceGrpc.cs create mode 100644 DysonNetwork.Pass/Leveling/ExperienceServiceGrpc.cs create mode 100644 DysonNetwork.Shared/Proto/leveling.proto diff --git a/DysonNetwork.Pass/Account/AccountController.cs b/DysonNetwork.Pass/Account/AccountController.cs index f556598..07b0909 100644 --- a/DysonNetwork.Pass/Account/AccountController.cs +++ b/DysonNetwork.Pass/Account/AccountController.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using DysonNetwork.Pass.Auth; +using DysonNetwork.Pass.Credit; using DysonNetwork.Pass.Wallet; using DysonNetwork.Shared.Error; using Microsoft.AspNetCore.Mvc; @@ -15,7 +16,8 @@ public class AccountController( AuthService auth, AccountService accounts, SubscriptionService subscriptions, - AccountEventService events + AccountEventService events, + SocialCreditService socialCreditService ) : ControllerBase { [HttpGet("{name}")] @@ -48,6 +50,25 @@ public class AccountController( .FirstOrDefaultAsync(); return account is null ? NotFound(ApiError.NotFound(name, traceId: HttpContext.TraceIdentifier)) : account.Badges.ToList(); } + + [HttpGet("{name}/credits")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> GetSocialCredits(string name) + { + var account = await db.Accounts + .Where(a => a.Name == name) + .Select(a => new { a.Id }) + .FirstOrDefaultAsync(); + + if (account is null) + { + return NotFound(ApiError.NotFound(name, traceId: HttpContext.TraceIdentifier)); + } + + var credits = await socialCreditService.GetSocialCredit(account.Id); + return credits; + } public class AccountCreateRequest { diff --git a/DysonNetwork.Pass/Credit/SocialCreditRecord.cs b/DysonNetwork.Pass/Credit/SocialCreditRecord.cs index 1815c64..de56891 100644 --- a/DysonNetwork.Pass/Credit/SocialCreditRecord.cs +++ b/DysonNetwork.Pass/Credit/SocialCreditRecord.cs @@ -4,6 +4,8 @@ using NodaTime; namespace DysonNetwork.Pass.Credit; +using Google.Protobuf.WellKnownTypes; + public class SocialCreditRecord : ModelBase { public Guid Id { get; set; } @@ -14,4 +16,20 @@ public class SocialCreditRecord : ModelBase public Guid AccountId { get; set; } public Account.Account Account { get; set; } = null!; + + public Shared.Proto.SocialCreditRecord ToProto() + { + var proto = new Shared.Proto.SocialCreditRecord + { + Id = Id.ToString(), + ReasonType = ReasonType, + Reason = Reason, + Delta = Delta, + AccountId = AccountId.ToString(), + CreatedAt = Timestamp.FromDateTimeOffset(CreatedAt.ToDateTimeOffset()), + UpdatedAt = Timestamp.FromDateTimeOffset(UpdatedAt.ToDateTimeOffset()) + }; + + return proto; + } } \ No newline at end of file diff --git a/DysonNetwork.Pass/Credit/SocialCreditServiceGrpc.cs b/DysonNetwork.Pass/Credit/SocialCreditServiceGrpc.cs new file mode 100644 index 0000000..c5d13b8 --- /dev/null +++ b/DysonNetwork.Pass/Credit/SocialCreditServiceGrpc.cs @@ -0,0 +1,27 @@ +using DysonNetwork.Shared.Proto; +using Grpc.Core; + +namespace DysonNetwork.Pass.Credit; + +public class SocialCreditServiceGrpc(SocialCreditService creditService) : Shared.Proto.SocialCreditService.SocialCreditServiceBase +{ + public override async Task AddRecord(AddSocialCreditRecordRequest request, ServerCallContext context) + { + var accountId = Guid.Parse(request.AccountId); + var record = await creditService.AddRecord( + request.ReasonType, + request.Reason, + request.Delta, + accountId); + + return record.ToProto(); + } + + public override async Task GetSocialCredit(GetSocialCreditRequest request, ServerCallContext context) + { + var accountId = Guid.Parse(request.AccountId); + var amount = await creditService.GetSocialCredit(accountId); + + return new SocialCreditResponse { Amount = amount }; + } +} \ No newline at end of file diff --git a/DysonNetwork.Pass/Leveling/ExperienceRecord.cs b/DysonNetwork.Pass/Leveling/ExperienceRecord.cs index f33429a..267a6b4 100644 --- a/DysonNetwork.Pass/Leveling/ExperienceRecord.cs +++ b/DysonNetwork.Pass/Leveling/ExperienceRecord.cs @@ -3,6 +3,8 @@ using DysonNetwork.Shared.Data; namespace DysonNetwork.Pass.Leveling; +using Google.Protobuf.WellKnownTypes; + public class ExperienceRecord : ModelBase { public Guid Id { get; set; } = Guid.NewGuid(); @@ -13,4 +15,21 @@ public class ExperienceRecord : ModelBase public Guid AccountId { get; set; } public Account.Account Account { get; set; } = null!; + + public Shared.Proto.ExperienceRecord ToProto() + { + var proto = new Shared.Proto.ExperienceRecord + { + Id = Id.ToString(), + ReasonType = ReasonType, + Reason = Reason, + Delta = Delta, + BonusMultiplier = BonusMultiplier, + AccountId = AccountId.ToString(), + CreatedAt = Timestamp.FromDateTimeOffset(CreatedAt.ToDateTimeOffset()), + UpdatedAt = Timestamp.FromDateTimeOffset(UpdatedAt.ToDateTimeOffset()) + }; + + return proto; + } } \ No newline at end of file diff --git a/DysonNetwork.Pass/Leveling/ExperienceServiceGrpc.cs b/DysonNetwork.Pass/Leveling/ExperienceServiceGrpc.cs new file mode 100644 index 0000000..89ec84e --- /dev/null +++ b/DysonNetwork.Pass/Leveling/ExperienceServiceGrpc.cs @@ -0,0 +1,19 @@ +using DysonNetwork.Shared.Proto; +using Grpc.Core; + +namespace DysonNetwork.Pass.Leveling; + +public class ExperienceServiceGrpc(ExperienceService experienceService) : Shared.Proto.ExperienceService.ExperienceServiceBase +{ + public override async Task AddRecord(AddExperienceRecordRequest request, ServerCallContext context) + { + var accountId = Guid.Parse(request.AccountId); + var record = await experienceService.AddRecord( + request.ReasonType, + request.Reason, + request.Delta, + accountId); + + return record.ToProto(); + } +} \ No newline at end of file diff --git a/DysonNetwork.Pass/Startup/ApplicationConfiguration.cs b/DysonNetwork.Pass/Startup/ApplicationConfiguration.cs index 38d299c..94f475a 100644 --- a/DysonNetwork.Pass/Startup/ApplicationConfiguration.cs +++ b/DysonNetwork.Pass/Startup/ApplicationConfiguration.cs @@ -1,6 +1,8 @@ using System.Net; using DysonNetwork.Pass.Account; using DysonNetwork.Pass.Auth; +using DysonNetwork.Pass.Credit; +using DysonNetwork.Pass.Leveling; using DysonNetwork.Pass.Permission; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.FileProviders; @@ -76,6 +78,8 @@ public static class ApplicationConfiguration app.MapGrpcService(); app.MapGrpcService(); app.MapGrpcService(); + app.MapGrpcService(); + app.MapGrpcService(); app.MapGrpcService(); return app; diff --git a/DysonNetwork.Shared/Proto/leveling.proto b/DysonNetwork.Shared/Proto/leveling.proto new file mode 100644 index 0000000..ea413f2 --- /dev/null +++ b/DysonNetwork.Shared/Proto/leveling.proto @@ -0,0 +1,83 @@ +syntax = "proto3"; + +package proto; + +option csharp_namespace = "DysonNetwork.Shared.Proto"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; + +// ==================================== +// Message Definitions +// ==================================== + +// SocialCreditRecord represents a record of social credit changes for an account +message SocialCreditRecord { + string id = 1; // UUID string + string reason_type = 2; + string reason = 3; + double delta = 4; + string account_id = 5; // UUID string + google.protobuf.Timestamp created_at = 6; + google.protobuf.Timestamp updated_at = 7; +} + +// ExperienceRecord represents a record of experience points gained by an account +message ExperienceRecord { + string id = 1; // UUID string + string reason_type = 2; + string reason = 3; + int64 delta = 4; + double bonus_multiplier = 5; + string account_id = 6; // UUID string + google.protobuf.Timestamp created_at = 7; + google.protobuf.Timestamp updated_at = 8; +} + +// ==================================== +// Request/Response Messages +// ==================================== + +// Social Credit Requests/Responses +message AddSocialCreditRecordRequest { + string reason_type = 1; + string reason = 2; + double delta = 3; + string account_id = 4; // UUID string +} + +message GetSocialCreditRequest { + string account_id = 1; // UUID string +} + +message SocialCreditResponse { + double amount = 1; +} + +// Experience Requests/Responses +message AddExperienceRecordRequest { + string reason_type = 1; + string reason = 2; + int64 delta = 3; + string account_id = 4; // UUID string +} + +// ==================================== +// Service Definitions +// ==================================== + +// SocialCreditService provides operations for managing social credit scores +service SocialCreditService { + // Adds a new social credit record for an account + rpc AddRecord(AddSocialCreditRecordRequest) returns (SocialCreditRecord); + + // Gets the current social credit score for an account + rpc GetSocialCredit(GetSocialCreditRequest) returns (SocialCreditResponse); +} + +// ExperienceService provides operations for managing experience points +service ExperienceService { + // Adds a new experience record for an account + rpc AddRecord(AddExperienceRecordRequest) returns (ExperienceRecord); +}