@
R3 Slice 2: Trading-Buchführung auf EF Core (+ InMemory-Tests) - BudgetService, TradeHistoryService, PortfolioService von Dapper/DatabaseService auf IDbContextFactory<CoreDbContext> (EF Core) umgestellt - Positionen/Budget/Trade-Historie ueber CoreDbContext (core_position/core_budget/core_trade_history) - Tests: EF-InMemory-Provider im Testprojekt; 5 PortfolioService-Tests (Buy/Sell/Avg/Exposure/Isolation) - 35/35 Tests + Build + smoke-ui gruen; WorkerBase-Log/Modul/Dapper-Entfernung folgen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
This commit is contained in:
@@ -1,48 +1,54 @@
|
||||
using IBKRTrader.Core.Database;
|
||||
using IBKRTrader.Core.Logging;
|
||||
using IBKRTrader.Core.Persistence.Ef;
|
||||
using IBKRTrader.Core.Persistence.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IBKRTrader.Core.Budget;
|
||||
|
||||
/// <summary>
|
||||
/// Verwaltet das Budget pro Modul.
|
||||
/// Liest/schreibt core_budget.
|
||||
/// Verwaltet das Budget pro Modul (Tabelle core_budget) via EF Core.
|
||||
/// </summary>
|
||||
public class BudgetService
|
||||
{
|
||||
private readonly DatabaseService _db;
|
||||
private readonly LoggingService _logger;
|
||||
private readonly IDbContextFactory<CoreDbContext> _dbf;
|
||||
private readonly LoggingService _logger;
|
||||
|
||||
public BudgetService(DatabaseService db, LoggingService logger)
|
||||
public BudgetService(IDbContextFactory<CoreDbContext> dbf, LoggingService logger)
|
||||
{
|
||||
_db = db;
|
||||
_dbf = dbf;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<decimal> GetAvailableBudgetAsync(string module)
|
||||
{
|
||||
var row = await _db.QueryFirstOrDefaultAsync<dynamic>(
|
||||
"SELECT total_budget, used_budget FROM `core_budget` WHERE module = @module",
|
||||
new { module });
|
||||
|
||||
if (row == null) return 0;
|
||||
return (decimal)row.total_budget - (decimal)row.used_budget;
|
||||
await using var db = await _dbf.CreateDbContextAsync();
|
||||
var b = await db.Budgets.FindAsync(module);
|
||||
return b is null ? 0m : b.TotalBudget - b.UsedBudget;
|
||||
}
|
||||
|
||||
public async Task ReserveBudgetAsync(string module, decimal amount)
|
||||
{
|
||||
await _db.ExecuteAsync(
|
||||
@"INSERT INTO `core_budget` (module, used_budget)
|
||||
VALUES (@module, @amount)
|
||||
ON DUPLICATE KEY UPDATE used_budget = used_budget + @amount",
|
||||
new { module, amount });
|
||||
await using var db = await _dbf.CreateDbContextAsync();
|
||||
var b = await db.Budgets.FindAsync(module);
|
||||
if (b is null)
|
||||
{
|
||||
db.Budgets.Add(new CoreBudget { Module = module, UsedBudget = amount, UpdatedAt = DateTime.UtcNow });
|
||||
}
|
||||
else
|
||||
{
|
||||
b.UsedBudget += amount;
|
||||
b.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task ReleaseBudgetAsync(string module, decimal amount)
|
||||
{
|
||||
await _db.ExecuteAsync(
|
||||
@"UPDATE `core_budget`
|
||||
SET used_budget = GREATEST(0, used_budget - @amount)
|
||||
WHERE module = @module",
|
||||
new { module, amount });
|
||||
await using var db = await _dbf.CreateDbContextAsync();
|
||||
var b = await db.Budgets.FindAsync(module);
|
||||
if (b is null) return;
|
||||
b.UsedBudget = Math.Max(0m, b.UsedBudget - amount);
|
||||
b.UpdatedAt = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user