Files
IBKRTrader/Modules/CongressTrading/Database/CongressRepository.cs
T
RichardandClaude Opus 4.8 ebeb035e92 Initial commit: IBKRTrader
.NET WinForms-Anwendung (Core, Modules/CongressTrading, UI).
Enthaelt .gitignore und settings.example.json als Konfigurationsvorlage.
Echte settings.json mit Zugangsdaten ist bewusst ausgeschlossen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 18:19:47 +02:00

175 lines
6.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Dapper;
using IBKRTrader.Core.Database;
using IBKRTrader.Core.Logging;
using IBKRTrader.Modules.CongressTrading.Models;
namespace IBKRTrader.Modules.CongressTrading.Database;
/// <summary>
/// Datenbankzugriff für das CongressTrading-Modul.
/// Kapselt alle INSERT/SELECT auf ct_congressMember und ct_trade.
/// </summary>
public class CongressRepository
{
private readonly DatabaseService _db;
private readonly LoggingService _logger;
public CongressRepository(DatabaseService db, LoggingService logger)
{
_db = db;
_logger = logger;
}
// ─── CongressMember ───────────────────────────────────────────────────────
public async Task<bool> MemberExistsAsync(string bioId)
{
var count = await _db.ExecuteScalarAsync<int>(
"SELECT COUNT(*) FROM `ct_congressMember` WHERE bio_id = @bioId",
new { bioId });
return count > 0;
}
public async Task UpsertMemberAsync(CongressMember member)
{
await _db.ExecuteAsync(@"
INSERT INTO `ct_congressMember` (bio_id, name, party, state, chamber, profile_url)
VALUES (@BioId, @Name, @Party, @State, @Chamber, @ProfileUrl)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
party = VALUES(party),
state = VALUES(state),
chamber = VALUES(chamber),
profile_url = VALUES(profile_url),
last_updated = CURRENT_TIMESTAMP", member);
}
public async Task<HashSet<string>> GetAllMemberBioIdsAsync()
{
var ids = await _db.QueryAsync<string>(
"SELECT bio_id FROM `ct_congressMember`");
return ids.ToHashSet();
}
// ─── CongressTrade ────────────────────────────────────────────────────────
public async Task<bool> TradeExistsAsync(string tradeId)
{
var count = await _db.ExecuteScalarAsync<int>(
"SELECT COUNT(*) FROM `ct_trade` WHERE trade_id = @tradeId",
new { tradeId });
return count > 0;
}
public async Task<HashSet<string>> GetAllTradeIdsAsync()
{
var ids = await _db.QueryAsync<string>(
"SELECT trade_id FROM `ct_trade`");
return ids.ToHashSet();
}
/// <summary>Fügt einen Trade ein. Ignoriert Duplikate (INSERT IGNORE).</summary>
public async Task InsertTradeAsync(CongressTrade trade, bool detailsFetched = true)
{
await _db.ExecuteAsync(@"
INSERT IGNORE INTO `ct_trade`
(trade_id, member_bio_id, issuer_name, issuer_id, ticker,
trade_type, chamber, owner, value,
trade_date, published_date, detail_url, details_fetched)
VALUES
(@TradeId, @MemberBioId, @IssuerName, @IssuerId, @Ticker,
@TradeType, @Chamber, @Owner, @Value,
@TradeDate, @PublishedDate, @DetailUrl, @DetailsFetched)",
new
{
trade.TradeId,
trade.MemberBioId,
trade.IssuerName,
trade.IssuerId,
trade.Ticker,
trade.TradeType,
trade.Chamber,
trade.Owner,
trade.Value,
TradeDate = trade.TradeDate?.ToString("yyyy-MM-dd"),
PublishedDate = trade.PublishedDate?.ToString("yyyy-MM-dd"),
trade.DetailUrl,
DetailsFetched = detailsFetched ? 1 : 0
});
}
/// <summary>Aktualisiert die Detail-Felder eines bereits vorhandenen Trades.</summary>
public async Task UpdateTradeDetailsAsync(CongressTrade trade)
{
await _db.ExecuteAsync(@"
UPDATE `ct_trade`
SET ticker = @Ticker,
trade_type = @TradeType,
trade_date = @TradeDate,
published_date = @PublishedDate,
details_fetched = 1
WHERE trade_id = @TradeId
AND details_fetched = 0",
new
{
trade.TradeId,
trade.Ticker,
trade.TradeType,
TradeDate = trade.TradeDate?.ToString("yyyy-MM-dd"),
PublishedDate = trade.PublishedDate?.ToString("yyyy-MM-dd")
});
}
/// <summary>Gibt Trade-IDs zurück für die noch keine Details geholt wurden.</summary>
public async Task<IEnumerable<string>> GetTradesWithoutDetailsAsync(int limit = 500)
=> await _db.QueryAsync<string>(
"SELECT trade_id FROM `ct_trade` WHERE details_fetched = 0 LIMIT @limit",
new { limit });
public async Task<int> GetTradeCountAsync()
=> await _db.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM `ct_trade`");
public async Task<int> GetMemberCountAsync()
=> await _db.ExecuteScalarAsync<int>("SELECT COUNT(*) FROM `ct_congressMember`");
// ─── core_settings Flags ──────────────────────────────────────────────────
public async Task<string?> GetSettingAsync(string key)
{
return await _db.QueryFirstOrDefaultAsync<string>(
"SELECT `value` FROM `core_settings` WHERE `key` = @key",
new { key });
}
public async Task SetSettingAsync(string key, string value)
{
await _db.ExecuteAsync(@"
INSERT INTO `core_settings` (`key`, `value`)
VALUES (@key, @value)
ON DUPLICATE KEY UPDATE `value` = @value",
new { key, value });
}
public async Task DeleteSettingAsync(string key)
{
await _db.ExecuteAsync(
"DELETE FROM `core_settings` WHERE `key` = @key", new { key });
}
// ─── Reset ────────────────────────────────────────────────────────────────
/// <summary>
/// Löscht alle CT-Fortschritts-Flags und alle CT-Daten aus der DB.
/// Danach startet der History-Import beim nächsten Worker-Run von Seite 1.
/// </summary>
public async Task ResetHistoryImportAsync()
{
await DeleteSettingAsync("ct.history_import_done");
await DeleteSettingAsync("ct.history_import_page");
await DeleteSettingAsync("ct.history_import_total");
await _db.ExecuteAsync("DELETE FROM `ct_trade`");
await _db.ExecuteAsync("DELETE FROM `ct_congressMember`");
_logger.Warn("CT", "History-Import-Reset durchgeführt alle CT-Daten gelöscht.");
}
}