Miner-Upgrade
All checks were successful
Deploy / deploy-staging (push) Successful in 7s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-05-09 00:58:48 +02:00
parent ee5a46254f
commit fc95898a9d
11 changed files with 976 additions and 131 deletions

View File

@@ -188,6 +188,10 @@ final class SchemaManager
$this->ensureMeasurementFxReferenceColumn();
$applied[] = 'measurement_fx_reference';
}
if ($this->tableExists($this->prefix . 'measurements') && !$this->columnExists($this->prefix . 'measurements', 'coin_currency')) {
$this->ensureMeasurementCoinCurrencyColumn();
$applied[] = 'measurement_coin_currency';
}
if (!$this->tableExists($this->prefix . 'measurement_rates')) {
$this->ensureMeasurementRatesTable();
@@ -197,6 +201,10 @@ final class SchemaManager
$this->ensurePayoutsTable();
$applied[] = 'payouts_table';
}
if (!$this->tableExists($this->prefix . 'wallet_snapshots')) {
$this->ensureWalletSnapshotsTable();
$applied[] = 'wallet_snapshots_table';
}
if (!$this->tableExists($this->prefix . 'fx_fetches') || !$this->tableExists($this->prefix . 'fx_rates')) {
$this->ensureFxRatesTable();
$applied[] = 'fx_rates_table';
@@ -293,6 +301,10 @@ final class SchemaManager
$this->ensureMeasurementFxReferenceColumn();
$applied[] = 'measurement_fx_reference';
}
if ($this->tableExists($this->prefix . 'measurements') && !$this->columnExists($this->prefix . 'measurements', 'coin_currency')) {
$this->ensureMeasurementCoinCurrencyColumn();
$applied[] = 'measurement_coin_currency';
}
if (!$this->tableExists($this->prefix . 'fx_fetches') || !$this->tableExists($this->prefix . 'fx_rates')) {
$this->ensureFxRatesTable();
@@ -308,6 +320,10 @@ final class SchemaManager
$this->ensurePayoutsTable();
$applied[] = 'payouts_table';
}
if (!$this->tableExists($this->prefix . 'wallet_snapshots')) {
$this->ensureWalletSnapshotsTable();
$applied[] = 'wallet_snapshots_table';
}
if (!$this->tableExists($this->prefix . 'miner_offers')) {
$this->upgradeMinerOffersTable();
@@ -421,6 +437,54 @@ final class SchemaManager
}
}
private function ensureMeasurementCoinCurrencyColumn(): void
{
$table = $this->prefix . 'measurements';
$settingsTable = $this->prefix . 'settings';
if (!$this->tableExists($table)) {
return;
}
$statements = $this->driver === 'pgsql'
? [
'ALTER TABLE ' . $table . ' ADD COLUMN IF NOT EXISTS coin_currency VARCHAR(10)',
'UPDATE ' . $table . ' AS m
SET coin_currency = COALESCE(NULLIF(BTRIM(s.crypto_currency), \'\'), \'DOGE\')
FROM ' . $settingsTable . ' AS s
WHERE s.project_key = m.project_key
AND (m.coin_currency IS NULL OR BTRIM(m.coin_currency) = \'\')',
'UPDATE ' . $table . ' SET coin_currency = \'DOGE\' WHERE coin_currency IS NULL OR BTRIM(coin_currency) = \'\'',
'ALTER TABLE ' . $table . ' ALTER COLUMN coin_currency SET DEFAULT \'DOGE\'',
'ALTER TABLE ' . $table . ' ALTER COLUMN coin_currency SET NOT NULL',
]
: [
'ALTER TABLE `' . $table . '` ADD COLUMN coin_currency VARCHAR(10) NOT NULL DEFAULT \'DOGE\' AFTER coins_total',
'UPDATE `' . $table . '` m
LEFT JOIN `' . $settingsTable . '` s ON s.project_key = m.project_key
SET m.coin_currency = COALESCE(NULLIF(TRIM(s.crypto_currency), \'\'), \'DOGE\')
WHERE m.coin_currency IS NULL OR TRIM(m.coin_currency) = \'\'',
];
foreach ($statements as $index => $statement) {
try {
if ($this->driver === 'mysql' && $index === 0 && $this->columnExists($table, 'coin_currency')) {
continue;
}
$this->executeUpgradeStatements([$statement], 'Messpunkt-Coin-Waehrung konnte nicht angelegt werden.');
} catch (\Throwable $exception) {
$message = strtolower($exception->getMessage());
if (
($this->driver === 'mysql' && $index === 0 && str_contains($message, 'duplicate column')) ||
($this->driver === 'pgsql' && str_contains($message, 'already exists'))
) {
continue;
}
throw $exception;
}
}
}
private function ensureLegacyMinerOfferImportColumns(): void
{
$table = $this->prefix . 'miner_offers';
@@ -599,6 +663,9 @@ final class SchemaManager
if ($this->tableExists($this->prefix . 'measurements') && !$this->columnExists($this->prefix . 'measurements', 'fx_fetch_id')) {
$upgrades[] = 'measurement_fx_reference';
}
if ($this->tableExists($this->prefix . 'measurements') && !$this->columnExists($this->prefix . 'measurements', 'coin_currency')) {
$upgrades[] = 'measurement_coin_currency';
}
if (!$this->tableExists($this->prefix . 'fx_fetches') || !$this->tableExists($this->prefix . 'fx_rates')) {
$upgrades[] = 'fx_rates_table';
@@ -609,6 +676,9 @@ final class SchemaManager
if (!$this->tableExists($this->prefix . 'payouts')) {
$upgrades[] = 'payouts_table';
}
if (!$this->tableExists($this->prefix . 'wallet_snapshots')) {
$upgrades[] = 'wallet_snapshots_table';
}
if (!$this->tableExists($this->prefix . 'miner_offers')) {
$upgrades[] = 'miner_offers_table';
}
@@ -849,6 +919,65 @@ final class SchemaManager
$this->executeUpgradeStatements($statements, 'Schema-Upgrade fuer Auszahlungen fehlgeschlagen.');
}
public function ensureWalletSnapshotsTable(): void
{
if ($this->tableExists($this->prefix . 'wallet_snapshots')) {
return;
}
$table = $this->prefix . 'wallet_snapshots';
$projectTable = $this->prefix . 'projects';
$statements = $this->driver === 'pgsql'
? [
'CREATE TABLE IF NOT EXISTS ' . $table . ' (
id BIGSERIAL PRIMARY KEY,
project_key VARCHAR(64) NOT NULL,
owner_sub VARCHAR(128) NOT NULL,
measured_at TIMESTAMP NOT NULL,
total_value_amount NUMERIC(20,8),
total_value_currency VARCHAR(10),
wallet_balance NUMERIC(28,10),
wallet_currency VARCHAR(10) NOT NULL,
balances_json JSONB,
note TEXT,
source VARCHAR(16) NOT NULL,
image_path VARCHAR(255),
ocr_raw_text TEXT,
ocr_confidence NUMERIC(6,4),
ocr_flags JSONB,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_mining_wallet_snapshots_project FOREIGN KEY (project_key) REFERENCES ' . $projectTable . '(project_key) ON DELETE CASCADE
)',
'CREATE INDEX IF NOT EXISTS idx_miningcheck_wallet_snapshots_project_measured_at ON ' . $table . ' (project_key, owner_sub, measured_at)',
]
: [
'CREATE TABLE IF NOT EXISTS `' . $table . '` (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
project_key VARCHAR(64) NOT NULL,
owner_sub VARCHAR(128) NOT NULL,
measured_at TIMESTAMP NOT NULL,
total_value_amount DECIMAL(20,8) NULL,
total_value_currency VARCHAR(10) NULL,
wallet_balance DECIMAL(28,10) NULL,
wallet_currency VARCHAR(10) NOT NULL,
balances_json JSON NULL,
note TEXT NULL,
source ENUM(\'manual\', \'image_ocr\', \'seed_import\') NOT NULL,
image_path VARCHAR(255) NULL,
ocr_raw_text MEDIUMTEXT NULL,
ocr_confidence DECIMAL(6,4) NULL,
ocr_flags JSON NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_mining_wallet_snapshots_project FOREIGN KEY (project_key) REFERENCES `' . $projectTable . '`(project_key) ON DELETE CASCADE,
KEY idx_miningcheck_wallet_snapshots_project_measured_at (project_key, owner_sub, measured_at)
)',
];
$this->executeUpgradeStatements($statements, 'Schema-Upgrade fuer Wallet-Snapshots fehlgeschlagen.');
}
public function ensureMinerTables(): void
{
if (!$this->tableExists($this->prefix . 'miner_offers')) {
@@ -1321,6 +1450,7 @@ final class SchemaManager
$this->prefix . 'fx_rates',
$this->prefix . 'measurement_rates',
$this->prefix . 'payouts',
$this->prefix . 'wallet_snapshots',
$this->prefix . 'miner_offers',
$this->prefix . 'purchased_miners',
];
@@ -1340,6 +1470,7 @@ final class SchemaManager
$this->prefix . 'measurements',
$this->prefix . 'measurement_rates',
$this->prefix . 'payouts',
$this->prefix . 'wallet_snapshots',
$this->prefix . 'miner_offers',
$this->prefix . 'targets',
$this->prefix . 'dashboard_definitions',