🐛 Fix Pass service missing perks subscription

This commit is contained in:
2026-02-04 02:10:31 +08:00
parent 0bc4ea68e1
commit 07c9c907f4
4 changed files with 209 additions and 6 deletions

View File

@@ -4,6 +4,7 @@ using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Networking;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -22,7 +23,8 @@ public class AccountCurrentController(
AccountEventService events,
AuthService auth,
FileService.FileServiceClient files,
Credit.SocialCreditService creditService
Credit.SocialCreditService creditService,
RemoteSubscriptionService remoteSubscription
) : ControllerBase
{
[HttpGet]
@@ -39,6 +41,24 @@ public class AccountCurrentController(
.Where(e => e.Id == userId)
.FirstOrDefaultAsync();
if (account != null)
{
// Populate PerkSubscription from Wallet service via gRPC
try
{
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
if (subscription != null)
{
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
}
}
catch (Exception ex)
{
// Log error but don't fail the request - PerkSubscription is optional
Console.WriteLine($"Failed to populate PerkSubscription for account {account.Id}: {ex.Message}");
}
}
return Ok(account);
}
@@ -321,7 +341,10 @@ public class AccountCurrentController(
}
else
{
if (currentUser.PerkSubscription is null)
// Check PerkSubscription via RemoteSubscriptionService instead of relying on currentUser.PerkSubscription
// which is not populated when currentUser comes from HttpContext.Items
var perkSubscription = await remoteSubscription.GetPerkSubscription(currentUser.Id);
if (perkSubscription == null)
return StatusCode(403, ApiError.Unauthorized(
message: "You need to have a subscription to check-in backdated.",
forbidden: true,

View File

@@ -1,6 +1,7 @@
using DysonNetwork.Pass.Credit;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Networking;
using DysonNetwork.Shared.Registry;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -10,7 +11,8 @@ namespace DysonNetwork.Pass.Account;
[Route("/api/accounts")]
public class AccountPublicController(
AppDatabase db,
SocialCreditService socialCreditService
SocialCreditService socialCreditService,
RemoteSubscriptionService remoteSubscription
) : ControllerBase
{
[HttpGet("{name}")]
@@ -26,6 +28,21 @@ public class AccountPublicController(
.FirstOrDefaultAsync();
if (account is null) return NotFound(ApiError.NotFound(name, traceId: HttpContext.TraceIdentifier));
// Populate PerkSubscription from Wallet service via gRPC
try
{
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
if (subscription != null)
{
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
}
}
catch (Exception ex)
{
// Log error but don't fail the request - PerkSubscription is optional
Console.WriteLine($"Failed to populate PerkSubscription for account {account.Id}: {ex.Message}");
}
return account;
}
@@ -67,11 +84,44 @@ public class AccountPublicController(
{
if (string.IsNullOrWhiteSpace(query))
return [];
return await db.Accounts
var accounts = await db.Accounts
.Include(e => e.Profile)
.Where(a => EF.Functions.ILike(a.Name, $"%{query}%") ||
EF.Functions.ILike(a.Nick, $"%{query}%"))
.Take(take)
.ToListAsync();
// Populate PerkSubscriptions from Wallet service via gRPC
if (accounts.Count > 0)
{
try
{
var accountIds = accounts.Select(a => a.Id).ToList();
var subscriptions = await remoteSubscription.GetPerkSubscriptions(accountIds);
var subscriptionDict = subscriptions
.Where(s => s != null)
.ToDictionary(
s => Guid.Parse(s.AccountId),
s => SnWalletSubscription.FromProtoValue(s).ToReference()
);
foreach (var account in accounts)
{
if (subscriptionDict.TryGetValue(account.Id, out var subscription))
{
account.PerkSubscription = subscription;
}
}
}
catch (Exception ex)
{
// Log error but don't fail the request - PerkSubscription is optional
Console.WriteLine($"Failed to populate PerkSubscriptions for search results: {ex.Message}");
}
}
return accounts;
}
}

View File

@@ -16,6 +16,7 @@ using NATS.Net;
using NodaTime;
using OtpNet;
using AuthService = DysonNetwork.Pass.Auth.AuthService;
using DysonNetwork.Shared.Registry;
namespace DysonNetwork.Pass.Account;
@@ -31,6 +32,7 @@ public class AccountService(
IStringLocalizer<EmailResource> emailLocalizer,
ICacheService cache,
ILogger<AccountService> logger,
RemoteSubscriptionService remoteSubscription,
INatsConnection nats
)
{
@@ -757,4 +759,56 @@ public class AccountService(
}).ToByteArray()
);
}
}
/// <summary>
/// Populates the PerkSubscription property for a single account by calling the Wallet service via gRPC.
/// </summary>
public async Task PopulatePerkSubscriptionAsync(SnAccount account)
{
try
{
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
if (subscription != null)
{
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
}
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to populate PerkSubscription for account {AccountId}", account.Id);
}
}
/// <summary>
/// Populates the PerkSubscription property for multiple accounts by calling the Wallet service via gRPC.
/// </summary>
public async Task PopulatePerkSubscriptionsAsync(List<SnAccount> accounts)
{
if (accounts.Count == 0) return;
try
{
var accountIds = accounts.Select(a => a.Id).ToList();
var subscriptions = await remoteSubscription.GetPerkSubscriptions(accountIds);
var subscriptionDict = subscriptions
.Where(s => s != null)
.ToDictionary(
s => Guid.Parse(s.AccountId),
s => SnWalletSubscription.FromProtoValue(s).ToReference()
);
foreach (var account in accounts)
{
if (subscriptionDict.TryGetValue(account.Id, out var subscription))
{
account.PerkSubscription = subscription;
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to populate PerkSubscriptions for {Count} accounts", accounts.Count);
}
}
}

View File

@@ -1,4 +1,6 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Microsoft.EntityFrameworkCore;
@@ -10,6 +12,7 @@ public class AccountServiceGrpc(
AppDatabase db,
AccountEventService accountEvents,
RelationshipService relationships,
RemoteSubscriptionService remoteSubscription,
ILogger<AccountServiceGrpc> logger
)
: Shared.Proto.AccountService.AccountServiceBase
@@ -33,6 +36,9 @@ public class AccountServiceGrpc(
if (account == null)
throw new RpcException(new Status(StatusCode.NotFound, $"Account {request.Id} not found"));
// Populate PerkSubscription from Wallet service via gRPC
await PopulatePerkSubscriptionAsync(account);
return account.ToProtoValue();
}
@@ -51,6 +57,9 @@ public class AccountServiceGrpc(
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound,
$"Account with automated ID {request.AutomatedId} not found"));
// Populate PerkSubscription from Wallet service via gRPC
await PopulatePerkSubscriptionAsync(account);
return account.ToProtoValue();
}
@@ -69,6 +78,9 @@ public class AccountServiceGrpc(
.Include(a => a.Profile)
.ToListAsync();
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
var response = new GetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
return response;
@@ -90,6 +102,9 @@ public class AccountServiceGrpc(
.Include(a => a.Profile)
.ToListAsync();
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
var response = new GetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
return response;
@@ -126,6 +141,9 @@ public class AccountServiceGrpc(
.Include(a => a.Profile)
.ToListAsync();
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
var response = new GetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
return response;
@@ -140,6 +158,9 @@ public class AccountServiceGrpc(
.Include(a => a.Profile)
.ToListAsync();
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
var response = new GetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
return response;
@@ -176,6 +197,9 @@ public class AccountServiceGrpc(
.Include(a => a.Profile)
.ToListAsync();
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
var response = new ListAccountsResponse
{
TotalSize = totalCount,
@@ -264,4 +288,56 @@ public class AccountServiceGrpc(
);
return new BoolValue { Value = hasRelationship };
}
}
/// <summary>
/// Populates the PerkSubscription property for a single account by calling the Wallet service via gRPC.
/// </summary>
private async Task PopulatePerkSubscriptionAsync(SnAccount account)
{
try
{
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
if (subscription != null)
{
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to populate PerkSubscription for account {AccountId} in gRPC service", account.Id);
}
}
/// <summary>
/// Populates the PerkSubscription property for multiple accounts by calling the Wallet service via gRPC.
/// </summary>
private async Task PopulatePerkSubscriptionsAsync(List<SnAccount> accounts)
{
if (accounts.Count == 0) return;
try
{
var accountIds = accounts.Select(a => a.Id).ToList();
var subscriptions = await remoteSubscription.GetPerkSubscriptions(accountIds);
var subscriptionDict = subscriptions
.Where(s => s != null)
.ToDictionary(
s => Guid.Parse(s.AccountId),
s => SnWalletSubscription.FromProtoValue(s).ToReference()
);
foreach (var account in accounts)
{
if (subscriptionDict.TryGetValue(account.Id, out var subscription))
{
account.PerkSubscription = subscription;
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to populate PerkSubscriptions for {Count} accounts in gRPC service", accounts.Count);
}
}
}