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

This commit is contained in:
2026-04-29 02:58:05 +02:00
parent 1ba8b550f6
commit 8bab6f9e26
6 changed files with 167 additions and 5 deletions

View File

@@ -79,6 +79,58 @@ final class FxRatesService
]);
}
public function snapshotByFetchId(int $fetchId, ?string $baseCurrency = null, ?array $symbols = null): ?array
{
if ($fetchId <= 0) {
return null;
}
$requestedBase = $this->normalizeCurrency($baseCurrency ?: $this->defaultBaseCurrency());
if ($requestedBase === '') {
return null;
}
$snapshot = $this->repository->getSnapshotByFetchId($fetchId, null);
if ($snapshot === null) {
return null;
}
return $this->localizeSnapshot($this->rebaseSnapshot($snapshot, $requestedBase, $symbols));
}
public function nearestSnapshot(?string $baseCurrency = null, string $at = '', ?array $symbols = null, ?int $windowMinutes = null): ?array
{
$timestamp = $this->normalizeTimestamp($at);
if ($timestamp === null) {
return null;
}
$requestedBase = $this->normalizeCurrency($baseCurrency ?: $this->defaultBaseCurrency());
if ($requestedBase === '') {
return null;
}
$nearest = $this->repository->findNearestFetch(null, $timestamp, $windowMinutes);
if ($nearest === null) {
return null;
}
$snapshot = $this->repository->getSnapshotByFetchId((int) ($nearest['id'] ?? 0), null);
if ($snapshot === null) {
return null;
}
$rebased = $this->rebaseSnapshot($snapshot, $requestedBase, $symbols);
if ($rebased === null) {
return null;
}
return $this->localizeSnapshot($rebased + [
'requested_at' => $timestamp,
'distance_seconds' => $nearest['distance_seconds'] ?? null,
]);
}
public function findRate(?string $fromCurrency, ?string $toCurrency, ?string $at = null, ?int $windowMinutes = null): ?array
{
$from = $this->normalizeCurrency($fromCurrency);
@@ -193,6 +245,13 @@ final class FxRatesService
return $result;
}
public function autoRefreshLatestRates(?string $baseCurrency = null, ?array $currencies = null, ?int $maxAgeMinutes = null): array
{
$minutes = $maxAgeMinutes ?? $this->refreshMaxAgeMinutes();
$hours = max(1, $minutes) / 60;
return $this->ensureFreshLatestRates($hours, $baseCurrency, $currencies);
}
public function history(string $fromCurrency, string $toCurrency, ?string $from = null, ?string $to = null, int $limit = 200): array
{
$fromCurrency = $this->normalizeCurrency($fromCurrency);
@@ -600,9 +659,14 @@ final class FxRatesService
}
if ($requestedBase === '' || $requestedBase === $snapshotBase) {
$filteredRates = $this->filterRates($rates, $symbols);
if ($this->symbolsContain($symbols, $requestedBase)) {
$filteredRates = [$requestedBase => 1.0] + $filteredRates;
}
return $snapshot + [
'base_currency' => $snapshotBase,
'rates' => $this->filterRates($rates, $symbols),
'rates' => $filteredRates,
];
}
@@ -620,9 +684,14 @@ final class FxRatesService
$rebasedRates[$code] = (float) $rate / (float) $baseRate;
}
$filteredRates = $this->filterRates($rebasedRates, $symbols);
if ($this->symbolsContain($symbols, $requestedBase)) {
$filteredRates = [$requestedBase => 1.0] + $filteredRates;
}
return $snapshot + [
'base_currency' => $requestedBase,
'rates' => $this->filterRates($rebasedRates, $symbols),
'rates' => $filteredRates,
'snapshot_base_currency' => $snapshotBase,
];
}
@@ -646,6 +715,21 @@ final class FxRatesService
return $filtered;
}
private function symbolsContain(?array $symbols, string $currency): bool
{
if (!is_array($symbols) || $symbols === []) {
return false;
}
foreach ($symbols as $symbol) {
if ($this->normalizeCurrency((string) $symbol) === $currency) {
return true;
}
}
return false;
}
private function crossHistory(string $fromCurrency, string $toCurrency, ?string $from = null, ?string $to = null, int $limit = 200): array
{
$fromAt = $this->normalizeTimestamp($from);
@@ -884,6 +968,11 @@ final class FxRatesService
return max(2, (int) ($this->settings['timeout_sec'] ?? 10));
}
private function refreshMaxAgeMinutes(): int
{
return max(1, (int) ($this->settings['refresh_max_age_minutes'] ?? 60));
}
private function defaultBaseCurrency(): string
{
return $this->normalizeCurrency((string) ($this->settings['default_base_currency'] ?? 'EUR')) ?: 'EUR';