prepare("SELECT id, payload_json, attempts FROM rewards_jobs WHERE status = 'queued' ORDER BY created_at ASC LIMIT :lim"); $stmt->bindValue(':lim', $limit, PDO::PARAM_INT); $stmt->execute(); $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($jobs as $job) { $jobId = (int) $job['id']; // optimistic lock $upd = $pdo->prepare("UPDATE rewards_jobs SET status = 'processing', attempts = attempts + 1 WHERE id = :id AND status = 'queued'"); $upd->execute([':id' => $jobId]); if ($upd->rowCount() === 0) continue; $payload = json_decode($job['payload_json'], true); if (!$payload) { $fail = $pdo->prepare("UPDATE rewards_jobs SET status = 'failed', last_error = :err WHERE id = :id"); $fail->execute([':id' => $jobId, ':err' => 'Invalid payload JSON']); continue; } // TODO: tutaj wstaw Waszą logikę nagród: // - policz nagrodę na podstawie stawki/rate, wyniku, itd. // - dopisz transakcje do tabeli `transactions` // - zaktualizuj `user_stats.balance` // - zwróć strukturę pod animacje w UI (np. coins, xp, items) $winnerId = (int)($payload['winnerUserId'] ?? 0); $loserId = (int)($payload['loserUserId'] ?? 0); $isDraw = !empty($payload['isDraw']); $leftUserId = (int)($payload['players']['left']['userId'] ?? 0); $rightUserId = (int)($payload['players']['right']['userId'] ?? 0); if ($isDraw && ($leftUserId <= 0 || $rightUserId <= 0)) { $fail = $pdo->prepare("UPDATE rewards_jobs SET status = 'failed', last_error = :err WHERE id = :id"); $fail->execute([':id' => $jobId, ':err' => 'Draw payload missing players.left/right userId']); continue; } if (!$isDraw && ($winnerId <= 0 || $loserId <= 0)) { $fail = $pdo->prepare("UPDATE rewards_jobs SET status = 'failed', last_error = :err WHERE id = :id"); $fail->execute([':id' => $jobId, ':err' => 'Payload missing winnerUserId/loserUserId']); continue; } // Minimalny przykład: +1.00 dla zwycięzcy, +0.20 dla przegranego // TODO: podmień na Waszą logikę (stawka/rate/ligy/tabele nagród) $winnerReward = 1.00; $loserReward = 0.20; $drawRefund = 1.00; $matchId = (int)($payload['matchId'] ?? 0); $score = (string)($payload['score'] ?? ''); try { $pdo->beginTransaction(); // ensure user_stats exists $insStatsStmt = $pdo->prepare("INSERT IGNORE INTO user_stats (user_id, balance, matches_played, matches_won, matches_lost, matches_draw, tournaments_played, tournaments_won, leagues_participated, total_income, total_expenses, total_transactions, account_status) VALUES (?, 0, 0,0,0,0,0,0,0,0,0,0,'active')"); if ($isDraw) { $insStatsStmt->execute([$leftUserId]); $insStatsStmt->execute([$rightUserId]); } else { $insStatsStmt->execute([$winnerId]); $insStatsStmt->execute([$loserId]); } // Insert transactions (if table exists) // Schema inferred from mds/transactions_add_example.sql: (user_id, type, amount, title, description, category) $pdo->exec("CREATE TABLE IF NOT EXISTS transactions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, type VARCHAR(20) NOT NULL, amount DECIMAL(12,2) NOT NULL, title VARCHAR(255) NOT NULL, description TEXT NULL, category VARCHAR(50) NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_created (user_id, created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); $tx = $pdo->prepare("INSERT INTO transactions (user_id, type, amount, title, description, category) VALUES (?, 'income', ?, ?, ?, ?)"); if ($isDraw) { $updDraw = $pdo->prepare("UPDATE user_stats SET balance = balance + ?, matches_played = matches_played + 1, matches_draw = matches_draw + 1, total_income = total_income + ?, total_transactions = total_transactions + 1 WHERE user_id = ?"); $updDraw->execute([$drawRefund, $drawRefund, $leftUserId]); $updDraw->execute([$drawRefund, $drawRefund, $rightUserId]); $titleD = 'Ping-Pong 1v1 - remis (zwrot stawki)'; $descD = 'Mecz #' . $matchId . ' wynik ' . $score; $tx->execute([$leftUserId, $drawRefund, $titleD, $descD, 'match']); $tx->execute([$rightUserId, $drawRefund, $titleD, $descD, 'match']); $result = [ 'draw' => [ 'left' => ['userId' => $leftUserId, 'reward' => (float)$drawRefund, 'currency' => 'balance'], 'right' => ['userId' => $rightUserId, 'reward' => (float)$drawRefund, 'currency' => 'balance'], ], 'animation' => ['type' => 'coins', 'durationMs' => 2500], 'match' => ['matchId' => $matchId, 'score' => $score] ]; } else { // Update stats + balance for winner/loser only when match is decisive. $pdo->prepare("UPDATE user_stats SET balance = balance + ?, matches_played = matches_played + 1, matches_won = matches_won + 1, total_income = total_income + ?, total_transactions = total_transactions + 1 WHERE user_id = ?") ->execute([$winnerReward, $winnerReward, $winnerId]); $pdo->prepare("UPDATE user_stats SET balance = balance + ?, matches_played = matches_played + 1, matches_lost = matches_lost + 1, total_income = total_income + ?, total_transactions = total_transactions + 1 WHERE user_id = ?") ->execute([$loserReward, $loserReward, $loserId]); $titleW = 'Ping-Pong 1v1 - wygrana'; $descW = 'Mecz #' . $matchId . ' wynik ' . $score; $tx->execute([$winnerId, $winnerReward, $titleW, $descW, 'match']); $titleL = 'Ping-Pong 1v1 - udział'; $descL = 'Mecz #' . $matchId . ' wynik ' . $score; $tx->execute([$loserId, $loserReward, $titleL, $descL, 'match']); $result = [ 'winner' => ['userId' => $winnerId, 'reward' => (float)$winnerReward, 'currency' => 'balance'], 'loser' => ['userId' => $loserId, 'reward' => (float)$loserReward, 'currency' => 'balance'], 'animation' => ['type' => 'coins', 'durationMs' => 2500], 'match' => ['matchId' => $matchId, 'score' => $score] ]; } $ok = $pdo->prepare("UPDATE rewards_jobs SET status = 'done', result_json = :res, last_error = NULL WHERE id = :id"); $ok->execute([':id' => $jobId, ':res' => json_encode($result, JSON_UNESCAPED_UNICODE)]); $pdo->commit(); logLine("Job #$jobId done"); } catch (Throwable $e) { if ($pdo->inTransaction()) $pdo->rollBack(); $fail = $pdo->prepare("UPDATE rewards_jobs SET status = 'failed', last_error = :err WHERE id = :id"); $fail->execute([':id' => $jobId, ':err' => $e->getMessage()]); logLine("Job #$jobId failed: " . $e->getMessage()); } }