using IBKRTrader.Core.Database; using IBKRTrader.Core.Logging; namespace IBKRTrader.Modules.CongressTrading.Database; /// /// Legt die ct_xxx-Tabellen idempotent an. /// Namensschema: ct_{tabellenname} gemäß Architekturregeln. /// public class CongressMigrations { private readonly DatabaseService _db; private readonly LoggingService _logger; public CongressMigrations(DatabaseService db, LoggingService logger) { _db = db; _logger = logger; } public async Task RunAsync() { _logger.Info("CT", "Starte CongressTrading-Datenbankmigrationen..."); await CreateCongressMemberTableAsync(); await CreateCongressTradeTableAsync(); _logger.Info("CT", "CongressTrading-Migrationen abgeschlossen."); } private Task CreateCongressMemberTableAsync() => _db.ExecuteAsync(@" CREATE TABLE IF NOT EXISTS `ct_congressMember` ( `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, `bio_id` VARCHAR(20) NOT NULL UNIQUE, `name` VARCHAR(200), `party` VARCHAR(50), `state` VARCHAR(50), `chamber` VARCHAR(10), `profile_url` VARCHAR(500), `first_seen` DATETIME DEFAULT CURRENT_TIMESTAMP, `last_updated` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX `idx_bio_id` (`bio_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); private async Task CreateCongressTradeTableAsync() { // Tabelle anlegen await _db.ExecuteAsync(@" CREATE TABLE IF NOT EXISTS `ct_trade` ( `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, `trade_id` VARCHAR(20) NOT NULL UNIQUE, `member_bio_id` VARCHAR(20) NOT NULL, `issuer_name` VARCHAR(500), `issuer_id` VARCHAR(20), `ticker` VARCHAR(20), `trade_type` VARCHAR(50), `chamber` VARCHAR(20), `owner` VARCHAR(50), `value` DECIMAL(18,2), `size_range_low` BIGINT, `size_range_high` BIGINT, `trade_date` DATE, `published_date` DATE, `detail_url` VARCHAR(500), `details_fetched` TINYINT(1) NOT NULL DEFAULT 1, `scraped_at` DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX `idx_member` (`member_bio_id`), INDEX `idx_trade_date` (`trade_date`), INDEX `idx_ticker` (`ticker`), INDEX `idx_details` (`details_fetched`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); // Idempotente Spalten-Migrationen (für bestehende DBs) await _db.ExecuteAsync("ALTER TABLE `ct_trade` ADD COLUMN IF NOT EXISTS `details_fetched` TINYINT(1) NOT NULL DEFAULT 1;"); await _db.ExecuteAsync("ALTER TABLE `ct_trade` ADD COLUMN IF NOT EXISTS `chamber` VARCHAR(20);"); await _db.ExecuteAsync("ALTER TABLE `ct_trade` ADD COLUMN IF NOT EXISTS `owner` VARCHAR(50);"); await _db.ExecuteAsync("ALTER TABLE `ct_trade` ADD COLUMN IF NOT EXISTS `value` DECIMAL(18,2);"); await _db.ExecuteAsync("ALTER TABLE `ct_trade` ADD INDEX IF NOT EXISTS `idx_details` (`details_fetched`);"); } }