diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php index 999811b..6f7b09a 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php @@ -37,7 +37,11 @@ final class AdminDrawIndexController extends Controller /** @var LengthAwarePaginator $paginator */ $paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']); - return AdminApiList::jsonWith($paginator, fn (Draw $row) => $this->row($row), [ + $statsByDrawId = $this->aggregateListStats( + $paginator->getCollection()->pluck('id')->map(fn ($id) => (int) $id)->all(), + ); + + return AdminApiList::jsonWith($paginator, fn (Draw $row) => $this->row($row, $statsByDrawId), [ 'schedule' => [ 'timezone' => LotterySettings::drawTimezone(), 'interval_minutes' => LotterySettings::drawIntervalMinutes(), @@ -47,9 +51,58 @@ final class AdminDrawIndexController extends Controller ]); } - /** @return array */ - private function row(Draw $draw): array + /** + * @param list $drawIds + * @return array + */ + private function aggregateListStats(array $drawIds): array { + if ($drawIds === []) { + return []; + } + + $betByDraw = TicketOrder::query() + ->whereIn('draw_id', $drawIds) + ->groupBy('draw_id') + ->selectRaw('draw_id, COALESCE(SUM(total_actual_deduct), 0) AS total_bet') + ->pluck('total_bet', 'draw_id'); + + $payoutRows = TicketItem::query() + ->whereIn('draw_id', $drawIds) + ->groupBy('draw_id') + ->selectRaw( + 'draw_id, COALESCE(SUM(win_amount), 0) AS win, COALESCE(SUM(jackpot_win_amount), 0) AS jackpot', + ) + ->get() + ->keyBy('draw_id'); + + $stats = []; + foreach ($drawIds as $drawId) { + $bet = (int) ($betByDraw[$drawId] ?? $betByDraw[(string) $drawId] ?? 0); + $payoutRow = $payoutRows->get($drawId) ?? $payoutRows->get((string) $drawId); + $payout = (int) ($payoutRow->win ?? 0) + (int) ($payoutRow->jackpot ?? 0); + $stats[$drawId] = [ + 'total_bet_minor' => $bet, + 'total_payout_minor' => $payout, + 'profit_loss_minor' => $bet - $payout, + ]; + } + + return $stats; + } + + /** + * @param array $statsByDrawId + * @return array + */ + private function row(Draw $draw, array $statsByDrawId): array + { + $stats = $statsByDrawId[(int) $draw->id] ?? [ + 'total_bet_minor' => 0, + 'total_payout_minor' => 0, + 'profit_loss_minor' => 0, + ]; + return [ 'id' => (int) $draw->id, 'draw_no' => $draw->draw_no, @@ -66,14 +119,9 @@ final class AdminDrawIndexController extends Controller 'current_result_version' => (int) $draw->current_result_version, 'settle_version' => (int) $draw->settle_version, 'is_reopened' => (bool) $draw->is_reopened, - 'total_bet_minor' => (int) TicketOrder::query()->where('draw_id', $draw->id)->sum('total_actual_deduct'), - 'total_payout_minor' => (int) TicketItem::query()->where('draw_id', $draw->id)->sum('win_amount') - + (int) TicketItem::query()->where('draw_id', $draw->id)->sum('jackpot_win_amount'), - 'profit_loss_minor' => (int) TicketOrder::query()->where('draw_id', $draw->id)->sum('total_actual_deduct') - - ( - (int) TicketItem::query()->where('draw_id', $draw->id)->sum('win_amount') - + (int) TicketItem::query()->where('draw_id', $draw->id)->sum('jackpot_win_amount') - ), + 'total_bet_minor' => $stats['total_bet_minor'], + 'total_payout_minor' => $stats['total_payout_minor'], + 'profit_loss_minor' => $stats['profit_loss_minor'], 'updated_at' => $draw->updated_at?->toIso8601String(), ]; } diff --git a/database/migrations/2026_05_31_100000_add_query_performance_indexes.php b/database/migrations/2026_05_31_100000_add_query_performance_indexes.php new file mode 100644 index 0000000..09635c1 --- /dev/null +++ b/database/migrations/2026_05_31_100000_add_query_performance_indexes.php @@ -0,0 +1,53 @@ +index('draw_id', 'idx_ticket_orders_draw_id'); + }); + + Schema::table('wallet_txns', function (Blueprint $table): void { + $table->index(['player_id', 'id'], 'idx_wallet_txns_player_id'); + }); + + Schema::table('ticket_items', function (Blueprint $table): void { + $table->index(['player_id', 'id'], 'idx_ticket_items_player_id'); + }); + + Schema::table('draws', function (Blueprint $table): void { + $table->index(['business_date', 'draw_time'], 'idx_draws_business_date_draw_time'); + }); + } + + public function down(): void + { + Schema::table('draws', function (Blueprint $table): void { + $table->dropIndex('idx_draws_business_date_draw_time'); + }); + + Schema::table('ticket_items', function (Blueprint $table): void { + $table->dropIndex('idx_ticket_items_player_id'); + }); + + Schema::table('wallet_txns', function (Blueprint $table): void { + $table->dropIndex('idx_wallet_txns_player_id'); + }); + + Schema::table('ticket_orders', function (Blueprint $table): void { + $table->dropIndex('idx_ticket_orders_draw_id'); + }); + } +};