From 813dd868113e34a9407f1371433c2d55a6ce537b Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Wed, 20 May 2026 00:38:05 +0200 Subject: [PATCH] ycyxc --- .../src/Domain/AnalyticsService.php | 126 ++++++++++++++---- 1 file changed, 99 insertions(+), 27 deletions(-) diff --git a/modules/mining-checker/src/Domain/AnalyticsService.php b/modules/mining-checker/src/Domain/AnalyticsService.php index 185b5c3..ecd49b2 100644 --- a/modules/mining-checker/src/Domain/AnalyticsService.php +++ b/modules/mining-checker/src/Domain/AnalyticsService.php @@ -1189,44 +1189,116 @@ final class AnalyticsService } $dogePerDayPerMh = $dogePerDay / $currentHashrateMh; - $cumulativeRevenue = 0.0; $maxDays = 3650; + $horizonTs = $baseTs + ($maxDays * 86400); + $entries = []; - for ($day = 0; $day <= $maxDays; $day++) { - $dayHashrate = 0.0; - $dayTs = $baseTs + ($day * 86400); - foreach ($costPlans as $plan) { - if (!empty($plan['is_active']) && $this->entryIsCovered($plan, $dayTs)) { - $dayHashrate += $this->normalizeHashrateMh($plan['mining_speed_value'] ?? null, $plan['mining_speed_unit'] ?? null); - $dayHashrate += $this->normalizeHashrateMh($plan['bonus_speed_value'] ?? null, $plan['bonus_speed_unit'] ?? null); - } - } - foreach ($purchasedMiners as $miner) { - if ((array_key_exists('is_active', $miner) && empty($miner['is_active'])) || !$this->entryIsCovered($miner, $dayTs, 'purchased_at')) { - continue; - } - $dayHashrate += $this->normalizeHashrateMh($miner['mining_speed_value'] ?? null, $miner['mining_speed_unit'] ?? null); - $dayHashrate += $this->normalizeHashrateMh($miner['bonus_speed_value'] ?? null, $miner['bonus_speed_unit'] ?? null); - } - - if ($dayHashrate <= 0) { + foreach ($costPlans as $plan) { + if (empty($plan['is_active'])) { continue; } + $entries[] = ['data' => $plan, 'start_field' => 'starts_at']; + } - $dayRevenue = $dayHashrate * $dogePerDayPerMh * $pricePerCoin; - $cumulativeRevenue += $dayRevenue; - if ($cumulativeRevenue >= $remainingAmount) { - $etaTs = (int) round($baseTs + ($day * 86400)); - return [ - 'days' => (float) $day, - 'eta' => $this->formatUtcTimestamp($etaTs), - ]; + foreach ($purchasedMiners as $miner) { + if (array_key_exists('is_active', $miner) && empty($miner['is_active'])) { + continue; + } + $entries[] = ['data' => $miner, 'start_field' => 'purchased_at']; + } + + $events = [$baseTs, $horizonTs]; + foreach ($entries as $entryMeta) { + $entry = $entryMeta['data']; + $startField = $entryMeta['start_field']; + $startTs = $this->utcTimestamp((string) ($entry[$startField] ?? '')); + if ($startTs > $baseTs && $startTs < $horizonTs) { + $events[] = $startTs; + } + + $endTs = $this->entryCoverageEndTimestamp($entry, $startField); + if ($endTs !== null) { + $afterEndTs = $endTs + 1; + if ($afterEndTs > $baseTs && $afterEndTs < $horizonTs) { + $events[] = $afterEndTs; + } } } + $events = array_values(array_unique(array_map('intval', $events))); + sort($events, SORT_NUMERIC); + + $cumulativeRevenue = 0.0; + for ($index = 0, $maxIndex = count($events) - 1; $index < $maxIndex; $index++) { + $segmentStartTs = $events[$index]; + $segmentEndTs = $events[$index + 1]; + if ($segmentEndTs <= $segmentStartTs) { + continue; + } + + $segmentHashrate = 0.0; + foreach ($entries as $entryMeta) { + $entry = $entryMeta['data']; + $startField = $entryMeta['start_field']; + if (!$this->entryIsCovered($entry, $segmentStartTs, $startField)) { + continue; + } + + $segmentHashrate += $this->normalizeHashrateMh($entry['mining_speed_value'] ?? null, $entry['mining_speed_unit'] ?? null); + $segmentHashrate += $this->normalizeHashrateMh($entry['bonus_speed_value'] ?? null, $entry['bonus_speed_unit'] ?? null); + } + + if ($segmentHashrate <= 0) { + continue; + } + + $segmentDays = ($segmentEndTs - $segmentStartTs) / 86400; + if ($segmentDays <= 0) { + continue; + } + + $segmentRevenuePerDay = $segmentHashrate * $dogePerDayPerMh * $pricePerCoin; + if ($segmentRevenuePerDay <= 0) { + continue; + } + + $segmentRevenue = $segmentRevenuePerDay * $segmentDays; + if ($cumulativeRevenue + $segmentRevenue >= $remainingAmount) { + $remainingSegmentAmount = $remainingAmount - $cumulativeRevenue; + $segmentOffsetDays = $remainingSegmentAmount / $segmentRevenuePerDay; + $etaTs = (int) round($segmentStartTs + ($segmentOffsetDays * 86400)); + return [ + 'days' => round(($etaTs - $baseTs) / 86400, 4), + 'eta' => $this->formatUtcTimestamp($etaTs), + ]; + } + + $cumulativeRevenue += $segmentRevenue; + } + return ['days' => null, 'eta' => null]; } + private function entryCoverageEndTimestamp(array $entry, string $startField = 'starts_at'): ?int + { + if (!empty($entry['auto_renew'])) { + return null; + } + + $runtimeMonths = (int) ($entry['runtime_months'] ?? 0); + if ($runtimeMonths <= 0) { + return null; + } + + $startTs = $this->utcTimestamp((string) ($entry[$startField] ?? '')); + if ($startTs <= 0) { + return null; + } + + $runtimeDays = $runtimeMonths * 30.4375; + return (int) round($startTs + ($runtimeDays * 86400)); + } + private function utcTimestamp(?string $value): int { $normalized = trim((string) $value);