checkWebLogin(); header('Content-Type: application/json'); $userId = $this->getCurrentUserId(); $data = json_decode(file_get_contents('php://input'), true); if (empty($data['bets']) || empty($data['period_number']) || empty($data['game_id'])) { echo json_encode(['success' => false, 'message' => 'Missing required fields']); return; } $gameId = (int)$data['game_id']; $periodNumber = $data['period_number']; $bets = $data['bets']; $db = new Database(); // 验证期号 $period = $db->get('periods', '*', ['period_number' => $periodNumber, 'game_id' => $gameId]); if (!$period || $period['status'] !== 'pending') { echo json_encode(['success' => false, 'message' => 'Period is not open for betting']); return; } // 赔率映射 $oddsList = $db->select('game_odds', '*', ['game_id' => $gameId]); $oddsMap = []; foreach ($oddsList as $o) { $oddsMap[$o['type'] . '_' . $o['target']] = (float)$o['odds']; } // 用户信息 $user = $db->get('users', '*', ['id' => $userId]); $isVirtual = (int)($user['is_virtual'] ?? 0); // 代理专属赔率覆盖 if (!empty($user['agent_id'])) { $agentOdds = $db->select('agent_odds', '*', ['agent_id' => $user['agent_id'], 'game_id' => $gameId]); foreach ($agentOdds as $ao) { $oddsMap[$ao['bet_type'] . '_' . $ao['bet_target']] = (float)$ao['odds']; } } // 限额 $limitsRaw = $db->select('bet_limits', '*', ['game_id' => $gameId]); $limits = []; foreach ($limitsRaw as $l) { $limits[$l['bet_type']] = $l; } // 验证投注 $totalAmount = 0; $validBets = []; foreach ($bets as $bet) { $type = $bet['type'] ?? ''; $target = $bet['value'] ?? $bet['target'] ?? ''; $amount = floatval($bet['amount'] ?? 0); if ($amount <= 0) continue; $key = $type . '_' . $target; $odds = $oddsMap[$key] ?? null; if ($odds === null) { echo json_encode(['success' => false, 'message' => "Invalid bet: $key"]); return; } // 限额检查 if (isset($limits[$type])) { if ($amount < (float)$limits[$type]['min_amount']) { echo json_encode(['success' => false, 'message' => "Min bet for $type: " . $limits[$type]['min_amount']]); return; } if ($amount > (float)$limits[$type]['max_amount']) { echo json_encode(['success' => false, 'message' => "Max bet for $type: " . $limits[$type]['max_amount']]); return; } } $totalAmount += $amount; $validBets[] = ['type' => $type, 'target' => $target, 'amount' => $amount, 'odds' => $odds]; } if (empty($validBets)) { echo json_encode(['success' => false, 'message' => 'No valid bets']); return; } // 事务 try { $db->medoo->pdo->beginTransaction(); $stmt = $db->medoo->pdo->prepare("SELECT balance FROM users WHERE id = :id FOR UPDATE"); $stmt->execute([':id' => $userId]); $userRow = $stmt->fetch(\PDO::FETCH_ASSOC); $currentBalance = (float)$userRow['balance']; if ($currentBalance < $totalAmount) { $db->medoo->pdo->rollBack(); echo json_encode(['success' => false, 'message' => 'Insufficient balance']); return; } $newBalance = $currentBalance - $totalAmount; $db->update('users', ['balance' => $newBalance], ['id' => $userId]); $db->insert('transactions', [ 'user_id' => $userId, 'type' => 'bet', 'amount' => -$totalAmount, 'balance_before' => $currentBalance, 'balance_after' => $newBalance, 'related_id' => $period['id'], 'description' => ($db->get('games', 'name', ['id' => $gameId]) ?: 'Game') . ' Bet - ' . $periodNumber, 'is_virtual' => $isVirtual, 'created_at' => date('Y-m-d H:i:s'), ]); foreach ($validBets as $vb) { $db->insert('bets', [ 'user_id' => $userId, 'game_id' => $gameId, 'period_number' => $periodNumber, 'period_id' => $period['id'], 'bet_type' => $vb['type'], 'bet_value' => $vb['target'], 'amount' => $vb['amount'], 'odds' => $vb['odds'], 'status' => 'pending', 'is_virtual' => $isVirtual, 'agent_id' => $user['agent_id'] ?? null, 'created_at' => date('Y-m-d H:i:s'), ]); } $db->medoo->pdo->commit(); echo json_encode(['success' => true, 'message' => 'Bet placed successfully', 'new_balance' => $newBalance]); } catch (\Throwable $e) { if ($db->medoo->pdo->inTransaction()) $db->medoo->pdo->rollBack(); echo json_encode(['success' => false, 'message' => 'System error']); } } }