🐛 Fix dozens of issue in PaymentServiceGrpc

This commit is contained in:
2025-11-01 12:37:39 +08:00
parent 0b65bf8dd7
commit ccd9dbcdbf
3 changed files with 95 additions and 57 deletions

View File

@@ -8,15 +8,24 @@ using WalletService = DysonNetwork.Shared.Proto.WalletService;
namespace DysonNetwork.Insight.Thought; namespace DysonNetwork.Insight.Thought;
public class ThoughtService(AppDatabase db, ICacheService cache, PaymentService.PaymentServiceClient paymentService, WalletService.WalletServiceClient walletService) public class ThoughtService(
AppDatabase db,
ICacheService cache,
PaymentService.PaymentServiceClient paymentService,
WalletService.WalletServiceClient walletService
)
{ {
public async Task<SnThinkingSequence?> GetOrCreateSequenceAsync(Guid accountId, Guid? sequenceId, public async Task<SnThinkingSequence?> GetOrCreateSequenceAsync(
string? topic = null) Guid accountId,
Guid? sequenceId,
string? topic = null
)
{ {
if (sequenceId.HasValue) if (sequenceId.HasValue)
{ {
var seq = await db.ThinkingSequences.FindAsync(sequenceId.Value); var seq = await db.ThinkingSequences.FindAsync(sequenceId.Value);
if (seq == null || seq.AccountId != accountId) return null; if (seq == null || seq.AccountId != accountId)
return null;
return seq; return seq;
} }
else else
@@ -46,7 +55,7 @@ public class ThoughtService(AppDatabase db, ICacheService cache, PaymentService.
Role = role, Role = role,
TokenCount = tokenCount, TokenCount = tokenCount,
ModelName = model, ModelName = model,
Chunks = chunks ?? new List<SnThinkingChunk>() Chunks = chunks ?? new List<SnThinkingChunk>(),
}; };
db.ThinkingThoughts.Add(thought); db.ThinkingThoughts.Add(thought);
@@ -65,25 +74,35 @@ public class ThoughtService(AppDatabase db, ICacheService cache, PaymentService.
public async Task<List<SnThinkingThought>> GetPreviousThoughtsAsync(SnThinkingSequence sequence) public async Task<List<SnThinkingThought>> GetPreviousThoughtsAsync(SnThinkingSequence sequence)
{ {
var cacheKey = $"thoughts:{sequence.Id}"; var cacheKey = $"thoughts:{sequence.Id}";
var (found, cachedThoughts) = await cache.GetAsyncWithStatus<List<SnThinkingThought>>(cacheKey); var (found, cachedThoughts) = await cache.GetAsyncWithStatus<List<SnThinkingThought>>(
cacheKey
);
if (found && cachedThoughts != null) if (found && cachedThoughts != null)
{ {
return cachedThoughts; return cachedThoughts;
} }
var thoughts = await db.ThinkingThoughts var thoughts = await db
.Where(t => t.SequenceId == sequence.Id) .ThinkingThoughts.Where(t => t.SequenceId == sequence.Id)
.OrderByDescending(t => t.CreatedAt) .OrderByDescending(t => t.CreatedAt)
.ToListAsync(); .ToListAsync();
// Cache for 10 minutes // Cache for 10 minutes
await cache.SetWithGroupsAsync(cacheKey, thoughts, [$"sequence:{sequence.Id}"], TimeSpan.FromMinutes(10)); await cache.SetWithGroupsAsync(
cacheKey,
thoughts,
[$"sequence:{sequence.Id}"],
TimeSpan.FromMinutes(10)
);
return thoughts; return thoughts;
} }
public async Task<(int total, List<SnThinkingSequence> sequences)> ListSequencesAsync(Guid accountId, int offset, public async Task<(int total, List<SnThinkingSequence> sequences)> ListSequencesAsync(
int take) Guid accountId,
int offset,
int take
)
{ {
var query = db.ThinkingSequences.Where(s => s.AccountId == accountId); var query = db.ThinkingSequences.Where(s => s.AccountId == accountId);
var totalCount = await query.CountAsync(); var totalCount = await query.CountAsync();
@@ -98,8 +117,8 @@ public class ThoughtService(AppDatabase db, ICacheService cache, PaymentService.
public async Task SettleThoughtBills(ILogger logger) public async Task SettleThoughtBills(ILogger logger)
{ {
var sequences = await db.ThinkingSequences var sequences = await db
.Where(s => s.PaidToken < s.TotalToken) .ThinkingSequences.Where(s => s.PaidToken < s.TotalToken)
.ToListAsync(); .ToListAsync();
if (sequences.Count == 0) if (sequences.Count == 0)
@@ -117,29 +136,32 @@ public class ThoughtService(AppDatabase db, ICacheService cache, PaymentService.
var totalUnpaidTokens = accountGroup.Sum(s => s.TotalToken - s.PaidToken); var totalUnpaidTokens = accountGroup.Sum(s => s.TotalToken - s.PaidToken);
var cost = (long)Math.Ceiling(totalUnpaidTokens / 1000.0); var cost = (long)Math.Ceiling(totalUnpaidTokens / 1000.0);
if (cost == 0) continue; if (cost == 0)
continue;
try try
{ {
var walletResponse = await walletService.GetWalletAsync(new GetWalletRequest { AccountId = accountId.ToString() });
var walletId = Guid.Parse(walletResponse.Id);
var date = DateTime.Now.ToString("yyyy-MM-dd"); var date = DateTime.Now.ToString("yyyy-MM-dd");
await paymentService.CreateTransactionAsync(new CreateTransactionRequest await paymentService.CreateTransactionWithAccountAsync(
{ new CreateTransactionWithAccountRequest
PayerWalletId = walletId.ToString(), {
PayeeWalletId = null, PayeeAccountId = accountId.ToString(),
Currency = WalletCurrency.SourcePoint, Currency = WalletCurrency.SourcePoint,
Amount = cost.ToString(), Amount = cost.ToString(),
Remarks = $"Wage for SN-chan on {date}", Remarks = $"Wage for SN-chan on {date}",
Type = TransactionType.System Type = TransactionType.System,
}); }
);
// Mark all sequences for this account as paid // Mark all sequences for this account as paid
foreach (var sequence in accountGroup) foreach (var sequence in accountGroup)
sequence.PaidToken = sequence.TotalToken; sequence.PaidToken = sequence.TotalToken;
logger.LogInformation("Billed {cost} points for account {accountId}", cost, accountId); logger.LogInformation(
"Billed {cost} points for account {accountId}",
cost,
accountId
);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -5,15 +5,21 @@ using NodaTime;
namespace DysonNetwork.Pass.Wallet; namespace DysonNetwork.Pass.Wallet;
public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.PaymentService.PaymentServiceBase public class PaymentServiceGrpc(PaymentService paymentService)
: Shared.Proto.PaymentService.PaymentServiceBase
{ {
public override async Task<Shared.Proto.Order> CreateOrder(CreateOrderRequest request, ServerCallContext context) public override async Task<Shared.Proto.Order> CreateOrder(
CreateOrderRequest request,
ServerCallContext context
)
{ {
var order = await paymentService.CreateOrderAsync( var order = await paymentService.CreateOrderAsync(
request.HasPayeeWalletId ? Guid.Parse(request.PayeeWalletId) : null, request.HasPayeeWalletId ? Guid.Parse(request.PayeeWalletId) : null,
request.Currency, request.Currency,
decimal.Parse(request.Amount), decimal.Parse(request.Amount),
request.Expiration is not null ? Duration.FromSeconds(request.Expiration.Seconds) : null, request.Expiration is not null
? Duration.FromSeconds(request.Expiration.Seconds)
: null,
request.HasAppIdentifier ? request.AppIdentifier : SnWalletOrder.InternalAppIdentifier, request.HasAppIdentifier ? request.AppIdentifier : SnWalletOrder.InternalAppIdentifier,
request.HasProductIdentifier ? request.ProductIdentifier : null, request.HasProductIdentifier ? request.ProductIdentifier : null,
request.HasRemarks ? request.Remarks : null, request.HasRemarks ? request.Remarks : null,
@@ -26,50 +32,65 @@ public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.Pa
} }
public override async Task<Shared.Proto.Transaction> CreateTransactionWithAccount( public override async Task<Shared.Proto.Transaction> CreateTransactionWithAccount(
CreateTransactionWithAccountRequest request, ServerCallContext context) CreateTransactionWithAccountRequest request,
ServerCallContext context
)
{ {
var transaction = await paymentService.CreateTransactionWithAccountAsync( var transaction = await paymentService.CreateTransactionWithAccountAsync(
request.HasPayerAccountId ? Guid.Parse(request.PayerAccountId) : null, request.PayerAccountId is not null ? Guid.Parse(request.PayerAccountId) : null,
request.HasPayeeAccountId ? Guid.Parse(request.PayeeAccountId) : null, request.PayeeAccountId is not null ? Guid.Parse(request.PayeeAccountId) : null,
request.Currency, request.Currency,
decimal.Parse(request.Amount), decimal.Parse(request.Amount),
request.HasRemarks ? request.Remarks : null, request.Remarks is not null ? request.Remarks : null,
(Shared.Models.TransactionType)request.Type (Shared.Models.TransactionType)request.Type
); );
return transaction.ToProtoValue(); return transaction.ToProtoValue();
} }
public override async Task<Shared.Proto.Transaction> CreateTransaction(CreateTransactionRequest request, public override async Task<Shared.Proto.Transaction> CreateTransaction(
ServerCallContext context) CreateTransactionRequest request,
ServerCallContext context
)
{ {
var transaction = await paymentService.CreateTransactionAsync( var transaction = await paymentService.CreateTransactionAsync(
request.HasPayerWalletId ? Guid.Parse(request.PayerWalletId) : null, request.PayerWalletId is not null ? Guid.Parse(request.PayerWalletId) : null,
request.HasPayeeWalletId ? Guid.Parse(request.PayeeWalletId) : null, request.PayeeWalletId is not null ? Guid.Parse(request.PayeeWalletId) : null,
request.Currency, request.Currency,
decimal.Parse(request.Amount), decimal.Parse(request.Amount),
request.HasRemarks ? request.Remarks : null, request.Remarks is not null ? request.Remarks : null,
(Shared.Models.TransactionType)request.Type (Shared.Models.TransactionType)request.Type
); );
return transaction.ToProtoValue(); return transaction.ToProtoValue();
} }
public override async Task<Shared.Proto.Order> CancelOrder(CancelOrderRequest request, ServerCallContext context) public override async Task<Shared.Proto.Order> CancelOrder(
CancelOrderRequest request,
ServerCallContext context
)
{ {
var order = await paymentService.CancelOrderAsync(Guid.Parse(request.OrderId)); var order = await paymentService.CancelOrderAsync(Guid.Parse(request.OrderId));
return order.ToProtoValue(); return order.ToProtoValue();
} }
public override async Task<RefundOrderResponse> RefundOrder(RefundOrderRequest request, ServerCallContext context) public override async Task<RefundOrderResponse> RefundOrder(
RefundOrderRequest request,
ServerCallContext context
)
{ {
var (order, refundTransaction) = await paymentService.RefundOrderAsync(Guid.Parse(request.OrderId)); var (order, refundTransaction) = await paymentService.RefundOrderAsync(
Guid.Parse(request.OrderId)
);
return new RefundOrderResponse return new RefundOrderResponse
{ {
Order = order.ToProtoValue(), Order = order.ToProtoValue(),
RefundTransaction = refundTransaction.ToProtoValue() RefundTransaction = refundTransaction.ToProtoValue(),
}; };
} }
public override async Task<Shared.Proto.Transaction> Transfer(TransferRequest request, ServerCallContext context) public override async Task<Shared.Proto.Transaction> Transfer(
TransferRequest request,
ServerCallContext context
)
{ {
var transaction = await paymentService.TransferAsync( var transaction = await paymentService.TransferAsync(
Guid.Parse(request.PayerAccountId), Guid.Parse(request.PayerAccountId),
@@ -80,3 +101,4 @@ public class PaymentServiceGrpc(PaymentService paymentService) : Shared.Proto.Pa
return transaction.ToProtoValue(); return transaction.ToProtoValue();
} }
} }

View File

@@ -188,7 +188,6 @@ service PaymentService {
rpc CreateOrder(CreateOrderRequest) returns (Order); rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc CreateTransactionWithAccount(CreateTransactionWithAccountRequest) returns (Transaction); rpc CreateTransactionWithAccount(CreateTransactionWithAccountRequest) returns (Transaction);
rpc CreateTransaction(CreateTransactionRequest) returns (Transaction); rpc CreateTransaction(CreateTransactionRequest) returns (Transaction);
rpc PayOrder(PayOrderRequest) returns (Order);
rpc CancelOrder(CancelOrderRequest) returns (Order); rpc CancelOrder(CancelOrderRequest) returns (Order);
rpc RefundOrder(RefundOrderRequest) returns (RefundOrderResponse); rpc RefundOrder(RefundOrderRequest) returns (RefundOrderResponse);
rpc Transfer(TransferRequest) returns (Transaction); rpc Transfer(TransferRequest) returns (Transaction);
@@ -252,28 +251,23 @@ enum TransactionType {
} }
message CreateTransactionWithAccountRequest { message CreateTransactionWithAccountRequest {
optional string payer_account_id = 1; google.protobuf.StringValue payer_account_id = 1;
optional string payee_account_id = 2; google.protobuf.StringValue payee_account_id = 2;
string currency = 3; string currency = 3;
string amount = 4; string amount = 4;
optional string remarks = 5; google.protobuf.StringValue remarks = 5;
TransactionType type = 6; TransactionType type = 6;
} }
message CreateTransactionRequest { message CreateTransactionRequest {
optional string payer_wallet_id = 1; google.protobuf.StringValue payer_wallet_id = 1;
optional string payee_wallet_id = 2; google.protobuf.StringValue payee_wallet_id = 2;
string currency = 3; string currency = 3;
string amount = 4; string amount = 4;
optional string remarks = 5; google.protobuf.StringValue remarks = 5;
TransactionType type = 6; TransactionType type = 6;
} }
message PayOrderRequest {
string order_id = 1;
string payer_wallet_id = 2;
}
message CancelOrderRequest { message CancelOrderRequest {
string order_id = 1; string order_id = 1;
} }