✨ Add billing
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PaymentService = DysonNetwork.Shared.Proto.PaymentService;
|
||||
using TransactionType = DysonNetwork.Shared.Proto.TransactionType;
|
||||
using WalletService = DysonNetwork.Shared.Proto.WalletService;
|
||||
|
||||
namespace DysonNetwork.Insight.Thought;
|
||||
|
||||
public class ThoughtService(AppDatabase db, ICacheService cache)
|
||||
public class ThoughtService(AppDatabase db, ICacheService cache, PaymentService.PaymentServiceClient paymentService, WalletService.WalletServiceClient walletService)
|
||||
{
|
||||
public async Task<SnThinkingSequence?> GetOrCreateSequenceAsync(Guid accountId, Guid? sequenceId,
|
||||
string? topic = null)
|
||||
@@ -28,17 +32,28 @@ public class ThoughtService(AppDatabase db, ICacheService cache)
|
||||
SnThinkingSequence sequence,
|
||||
string content,
|
||||
ThinkingThoughtRole role,
|
||||
List<SnThinkingChunk>? chunks = null
|
||||
List<SnThinkingChunk>? chunks = null,
|
||||
string? model = null
|
||||
)
|
||||
{
|
||||
// Approximate token count (1 token ≈ 4 characters for GPT-like models)
|
||||
var tokenCount = content?.Length / 4 ?? 0;
|
||||
|
||||
var thought = new SnThinkingThought
|
||||
{
|
||||
SequenceId = sequence.Id,
|
||||
Content = content,
|
||||
Role = role,
|
||||
Chunks = chunks ?? []
|
||||
TokenCount = tokenCount,
|
||||
ModelName = model,
|
||||
Chunks = chunks ?? new List<SnThinkingChunk>()
|
||||
};
|
||||
db.ThinkingThoughts.Add(thought);
|
||||
|
||||
// Update sequence total tokens only for assistant responses
|
||||
if (role == ThinkingThoughtRole.Assistant)
|
||||
sequence.TotalToken += tokenCount;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
// Invalidate cache for this sequence's thoughts
|
||||
@@ -80,4 +95,58 @@ public class ThoughtService(AppDatabase db, ICacheService cache)
|
||||
|
||||
return (totalCount, sequences);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SettleThoughtBills(ILogger logger)
|
||||
{
|
||||
var sequences = await db.ThinkingSequences
|
||||
.Where(s => s.PaidToken < s.TotalToken)
|
||||
.ToListAsync();
|
||||
|
||||
if (sequences.Count == 0)
|
||||
{
|
||||
logger.LogInformation("No unpaid sequences found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Group by account
|
||||
var groupedByAccount = sequences.GroupBy(s => s.AccountId);
|
||||
|
||||
foreach (var accountGroup in groupedByAccount)
|
||||
{
|
||||
var accountId = accountGroup.Key;
|
||||
var totalUnpaidTokens = accountGroup.Sum(s => s.TotalToken - s.PaidToken);
|
||||
var cost = (long)Math.Ceiling(totalUnpaidTokens / 1000.0);
|
||||
|
||||
if (cost == 0) continue;
|
||||
|
||||
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");
|
||||
await paymentService.CreateTransactionAsync(new CreateTransactionRequest
|
||||
{
|
||||
PayerWalletId = walletId.ToString(),
|
||||
PayeeWalletId = null,
|
||||
Currency = WalletCurrency.SourcePoint,
|
||||
Amount = cost.ToString(),
|
||||
Remarks = $"Wage for SN-chan on {date}",
|
||||
Type = TransactionType.System
|
||||
});
|
||||
|
||||
// Mark all sequences for this account as paid
|
||||
foreach (var sequence in accountGroup)
|
||||
sequence.PaidToken = sequence.TotalToken;
|
||||
|
||||
logger.LogInformation("Billed {cost} points for account {accountId}", cost, accountId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error billing for account {accountId}", accountId);
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user