WorldCupMarketController.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. namespace App\Http\Controllers\Admin;
  3. use App\Services\WorldCup\WorldCupBetService;
  4. use App\Services\WorldCup\WorldCupOddsService;
  5. use App\Services\WorldCup\WorldCupScheduleUpdateService;
  6. use App\Services\WorldCup\WorldCupSettlementService;
  7. use Illuminate\Http\Request;
  8. class WorldCupMarketController extends BaseController
  9. {
  10. private $oddsService;
  11. private $betService;
  12. private $scheduleService;
  13. private $settlementService;
  14. public function __construct(
  15. WorldCupOddsService $oddsService,
  16. WorldCupBetService $betService,
  17. WorldCupScheduleUpdateService $scheduleService,
  18. WorldCupSettlementService $settlementService
  19. ) {
  20. $this->oddsService = $oddsService;
  21. $this->betService = $betService;
  22. $this->scheduleService = $scheduleService;
  23. $this->settlementService = $settlementService;
  24. }
  25. public function odds(Request $request)
  26. {
  27. $filters = [
  28. 'market' => (string)$request->input('market', ''),
  29. 'match_id' => (string)$request->input('match_id', ''),
  30. 'limit' => (int)$request->input('limit', 300),
  31. ];
  32. if ($this->wantsJson($request)) {
  33. return $this->json(200, 'success', [
  34. 'list' => $this->oddsService->listOdds($filters),
  35. ]);
  36. }
  37. return view('admin.world_cup.odds', [
  38. 'filters' => $filters,
  39. 'odds' => $this->oddsService->listOdds($filters),
  40. 'matches' => $this->scheduleService->allMatches(),
  41. 'result' => $request->session()->get('world_cup_odds_result'),
  42. ]);
  43. }
  44. public function saveOdds(Request $request)
  45. {
  46. $result = $this->oddsService->saveOdds([
  47. 'market' => (string)$request->input('market', ''),
  48. 'match_id' => $request->input('match_id') === '' ? null : $request->input('match_id'),
  49. 'selection' => (string)$request->input('selection', ''),
  50. 'decimal_odds' => (float)$request->input('decimal_odds', 0),
  51. 'is_active' => (int)$request->input('is_active', 0),
  52. 'locked_weight' => (int)$request->input('locked_weight', 0),
  53. ]);
  54. return $this->respond($request, $result, '/admin/world-cup/odds', 'world_cup_odds_result');
  55. }
  56. public function importOdds(Request $request)
  57. {
  58. $rows = $this->parseCsvRows($request);
  59. $result = $rows === null
  60. ? [
  61. 'success' => false,
  62. 'message' => 'Invalid CSV file',
  63. 'data' => [
  64. 'updated' => 0,
  65. 'skipped' => 0,
  66. 'errors' => [],
  67. ],
  68. ]
  69. : $this->oddsService->importOddsRows(
  70. (string)$request->input('market', ''),
  71. $rows
  72. );
  73. return $this->respond($request, $result, '/admin/world-cup/odds', 'world_cup_odds_result');
  74. }
  75. public function bets(Request $request)
  76. {
  77. $result = $this->betService->adminBetLogs([
  78. 'game_id' => $request->input('game_id', ''),
  79. 'user_id' => $request->input('user_id', ''),
  80. 'status' => $request->input('status', ''),
  81. 'market' => $request->input('market', ''),
  82. 'limit' => (int)$request->input('limit', 100),
  83. ]);
  84. if ($this->wantsJson($request)) {
  85. return $this->json(200, 'success', [
  86. 'list' => $result['list'],
  87. ]);
  88. }
  89. return view('admin.world_cup.bets', [
  90. 'filters' => $result['filters'],
  91. 'list' => $result['list'],
  92. ]);
  93. }
  94. public function settlement(Request $request)
  95. {
  96. if ($this->wantsJson($request)) {
  97. return $this->json(200, 'success', [
  98. 'matches' => $this->scheduleService->allMatches(),
  99. ]);
  100. }
  101. return view('admin.world_cup.settlement', [
  102. 'matches' => $this->scheduleService->allMatches(),
  103. 'result' => $request->session()->get('world_cup_settlement_result'),
  104. ]);
  105. }
  106. public function settleMatch(Request $request)
  107. {
  108. $result = $this->settlementService->settleMatch(
  109. (int)$request->input('match_id', 0),
  110. (string)$request->input('result', ''),
  111. $this->actor($request)
  112. );
  113. return $this->respond($request, $result, '/admin/world-cup/settlement', 'world_cup_settlement_result');
  114. }
  115. public function settleWinner(Request $request)
  116. {
  117. $result = $this->settlementService->settleWinner(
  118. (string)$request->input('selection', ''),
  119. $this->actor($request)
  120. );
  121. return $this->respond($request, $result, '/admin/world-cup/settlement', 'world_cup_settlement_result');
  122. }
  123. private function respond(Request $request, array $result, string $url, string $sessionKey)
  124. {
  125. if ($this->wantsJson($request)) {
  126. return $this->json($result['success'] ? 200 : 400, $result['message'] ?: 'success', $result['data']);
  127. }
  128. return redirect($url)->with($sessionKey, $result);
  129. }
  130. private function actor(Request $request): string
  131. {
  132. return (string)($request->session()->get('admin.username') ?: 'admin');
  133. }
  134. private function wantsJson(Request $request): bool
  135. {
  136. return $request->ajax()
  137. || $request->expectsJson()
  138. || $request->input('format') === 'json';
  139. }
  140. private function parseCsvRows(Request $request): ?array
  141. {
  142. if (!$request->hasFile('csv_file') || !$request->file('csv_file')->isValid()) {
  143. return null;
  144. }
  145. $handle = fopen($request->file('csv_file')->getRealPath(), 'r');
  146. if ($handle === false) {
  147. return null;
  148. }
  149. $headers = null;
  150. $rows = [];
  151. while (($data = fgetcsv($handle)) !== false) {
  152. if ($headers === null) {
  153. $headers = $this->normalizeCsvHeaders($data);
  154. continue;
  155. }
  156. if ($this->isEmptyCsvRow($data)) {
  157. continue;
  158. }
  159. $values = array_slice(array_pad($data, count($headers), ''), 0, count($headers));
  160. $rows[] = array_combine($headers, $values);
  161. }
  162. fclose($handle);
  163. return $headers === null ? null : $rows;
  164. }
  165. private function normalizeCsvHeaders(array $headers): array
  166. {
  167. return array_map(function ($header) {
  168. return trim(str_replace("\xEF\xBB\xBF", '', (string)$header));
  169. }, $headers);
  170. }
  171. private function isEmptyCsvRow(array $row): bool
  172. {
  173. foreach ($row as $value) {
  174. if (trim((string)$value) !== '') {
  175. return false;
  176. }
  177. }
  178. return true;
  179. }
  180. }