ddfsdfdf
All checks were successful
Deploy / deploy-staging (push) Successful in 6s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-05-01 02:49:22 +02:00
parent d8f532ce0e
commit 6ce45f6d23
7 changed files with 180 additions and 31 deletions

View File

@@ -25,11 +25,13 @@ final class FxRatesRepository
$this->pdo->exec("CREATE TABLE IF NOT EXISTS {$fetchTable} (
id SERIAL PRIMARY KEY,
provider VARCHAR(64) NOT NULL,
trigger_source VARCHAR(32) NOT NULL DEFAULT 'manual',
base_currency VARCHAR(10) NOT NULL,
rate_date DATE NOT NULL,
fetched_at TIMESTAMP NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)");
$this->pdo->exec("ALTER TABLE {$fetchTable} ADD COLUMN IF NOT EXISTS trigger_source VARCHAR(32) NOT NULL DEFAULT 'manual'");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS {$rateTable} (
id SERIAL PRIMARY KEY,
fetch_id INTEGER NOT NULL REFERENCES {$fetchTable}(id) ON DELETE CASCADE,
@@ -44,6 +46,7 @@ final class FxRatesRepository
$this->pdo->exec("CREATE TABLE IF NOT EXISTS {$fetchTable} (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
provider VARCHAR(64) NOT NULL,
trigger_source VARCHAR(32) NOT NULL DEFAULT 'manual',
base_currency VARCHAR(10) NOT NULL,
rate_date DATE NOT NULL,
fetched_at DATETIME NOT NULL,
@@ -51,6 +54,7 @@ final class FxRatesRepository
KEY {$fetchTable}_base_fetch_idx (base_currency, fetched_at, id),
KEY {$fetchTable}_rate_date_idx (rate_date)
)");
$this->ensureColumn($fetchTable, 'trigger_source', "ALTER TABLE {$fetchTable} ADD COLUMN trigger_source VARCHAR(32) NOT NULL DEFAULT 'manual'");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS {$rateTable} (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
fetch_id INTEGER NOT NULL,
@@ -63,11 +67,13 @@ final class FxRatesRepository
$this->pdo->exec("CREATE TABLE IF NOT EXISTS {$fetchTable} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
provider VARCHAR(64) NOT NULL,
trigger_source VARCHAR(32) NOT NULL DEFAULT 'manual',
base_currency VARCHAR(10) NOT NULL,
rate_date DATE NOT NULL,
fetched_at DATETIME NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
)");
$this->ensureColumn($fetchTable, 'trigger_source', "ALTER TABLE {$fetchTable} ADD COLUMN trigger_source VARCHAR(32) NOT NULL DEFAULT 'manual'");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS {$rateTable} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fetch_id INTEGER NOT NULL,
@@ -83,7 +89,7 @@ final class FxRatesRepository
public function getLatestFetch(?string $baseCurrency = null): ?array
{
$sql = 'SELECT id, provider, base_currency, rate_date, fetched_at, created_at FROM ' . $this->table('fetches');
$sql = 'SELECT id, provider, trigger_source, base_currency, rate_date, fetched_at, created_at FROM ' . $this->table('fetches');
$params = [];
if ($baseCurrency !== null && trim($baseCurrency) !== '') {
$sql .= ' WHERE base_currency = :base_currency';
@@ -99,7 +105,7 @@ final class FxRatesRepository
public function listLatestFetches(): array
{
$stmt = $this->pdo->query(
'SELECT id, provider, base_currency, rate_date, fetched_at, created_at
'SELECT id, provider, trigger_source, base_currency, rate_date, fetched_at, created_at
FROM ' . $this->table('fetches') . '
ORDER BY fetched_at DESC, id DESC'
);
@@ -120,7 +126,7 @@ final class FxRatesRepository
public function listRecentFetches(int $limit = 20): array
{
$stmt = $this->pdo->prepare(
'SELECT id, provider, base_currency, rate_date, fetched_at, created_at
'SELECT id, provider, trigger_source, base_currency, rate_date, fetched_at, created_at
FROM ' . $this->table('fetches') . '
ORDER BY fetched_at DESC, id DESC
LIMIT :limit'
@@ -161,7 +167,7 @@ final class FxRatesRepository
foreach (['<=', '>='] as $operator) {
$order = $operator === '<=' ? 'DESC' : 'ASC';
$stmt = $this->pdo->prepare(
'SELECT id, provider, base_currency, rate_date, fetched_at, created_at
'SELECT id, provider, trigger_source, base_currency, rate_date, fetched_at, created_at
FROM ' . $this->table('fetches') . '
WHERE fetched_at ' . $operator . ' :target_at
ORDER BY fetched_at ' . $order . ', id ' . $order . '
@@ -277,11 +283,12 @@ final class FxRatesRepository
return array_map(fn (array $row): array => $this->normalizeRate($row), $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []);
}
public function saveFetch(string $baseCurrency, string $provider, string $rateDate, array $rates, ?string $fetchedAt = null): array
public function saveFetch(string $baseCurrency, string $provider, string $rateDate, array $rates, ?string $fetchedAt = null, string $triggerSource = 'manual'): array
{
$baseCurrency = strtoupper(trim($baseCurrency));
$provider = trim($provider) !== '' ? trim($provider) : 'currencyapi';
$fetchedAt = trim((string) $fetchedAt) !== '' ? trim((string) $fetchedAt) : gmdate('Y-m-d H:i:s');
$triggerSource = $this->normalizeTriggerSource($triggerSource);
$normalizedRates = [];
foreach ($rates as $currencyCode => $rate) {
$currencyCode = strtoupper(trim((string) $currencyCode));
@@ -301,14 +308,15 @@ final class FxRatesRepository
if ($this->driver === 'pgsql') {
$fetchStmt = $this->pdo->prepare(
'INSERT INTO ' . $this->table('fetches') . ' (
provider, base_currency, rate_date, fetched_at
provider, trigger_source, base_currency, rate_date, fetched_at
) VALUES (
:provider, :base_currency, :rate_date, :fetched_at
:provider, :trigger_source, :base_currency, :rate_date, :fetched_at
)
RETURNING *'
);
$fetchStmt->execute([
'provider' => $provider,
'trigger_source' => $triggerSource,
'base_currency' => $baseCurrency,
'rate_date' => $rateDate,
'fetched_at' => $fetchedAt,
@@ -317,13 +325,14 @@ final class FxRatesRepository
} else {
$fetchStmt = $this->pdo->prepare(
'INSERT INTO ' . $this->table('fetches') . ' (
provider, base_currency, rate_date, fetched_at
provider, trigger_source, base_currency, rate_date, fetched_at
) VALUES (
:provider, :base_currency, :rate_date, :fetched_at
:provider, :trigger_source, :base_currency, :rate_date, :fetched_at
)'
);
$fetchStmt->execute([
'provider' => $provider,
'trigger_source' => $triggerSource,
'base_currency' => $baseCurrency,
'rate_date' => $rateDate,
'fetched_at' => $fetchedAt,
@@ -379,7 +388,7 @@ final class FxRatesRepository
private function getFetchById(int $fetchId): ?array
{
$stmt = $this->pdo->prepare(
'SELECT id, provider, base_currency, rate_date, fetched_at, created_at
'SELECT id, provider, trigger_source, base_currency, rate_date, fetched_at, created_at
FROM ' . $this->table('fetches') . '
WHERE id = :id
LIMIT 1'
@@ -393,10 +402,10 @@ final class FxRatesRepository
{
$order = $operator === '<=' ? 'DESC' : 'ASC';
$stmt = $this->pdo->prepare(
'SELECT id, provider, base_currency, rate_date, fetched_at, created_at
'SELECT id, provider, trigger_source, base_currency, rate_date, fetched_at, created_at
FROM ' . $this->table('fetches') . '
WHERE base_currency = :base_currency
AND fetched_at ' . $operator . ' :target_at
AND fetched_at ' . $operator . ' :target_at
ORDER BY fetched_at ' . $order . ', id ' . $order . '
LIMIT 1'
);
@@ -456,6 +465,7 @@ final class FxRatesRepository
return [
'id' => isset($row['id']) ? (int) $row['id'] : null,
'provider' => (string) ($row['provider'] ?? ''),
'trigger_source' => (string) ($row['trigger_source'] ?? 'manual'),
'base_currency' => strtoupper((string) ($row['base_currency'] ?? '')),
'rate_date' => (string) ($row['rate_date'] ?? ''),
'fetched_at' => (string) ($row['fetched_at'] ?? ''),
@@ -463,6 +473,34 @@ final class FxRatesRepository
];
}
private function ensureColumn(string $table, string $column, string $alterSql): void
{
try {
$stmt = $this->pdo->query('SELECT * FROM ' . $table . ' LIMIT 1');
if ($stmt instanceof \PDOStatement) {
$row = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
if (in_array(strtolower($column), array_map('strtolower', array_keys($row)), true)) {
return;
}
}
} catch (\Throwable) {
}
try {
$this->pdo->exec($alterSql);
} catch (\Throwable) {
}
}
private function normalizeTriggerSource(string $source): string
{
$source = strtolower(trim($source));
return match ($source) {
'cron', 'manual', 'api' => $source,
default => 'manual',
};
}
private function normalizeRate(array $row): array
{
return [