From b649c862efd9e9ce7e9c26f88536324163679335 Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 27 May 2026 11:31:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8=20WalletBalanceController=20?= =?UTF-8?q?=E4=B8=AD=E9=9B=86=E6=88=90=E4=B8=BB=E7=AB=99=E9=92=B1=E5=8C=85?= =?UTF-8?q?=E4=BD=99=E9=A2=9D=E8=8E=B7=E5=8F=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 HttpMainSiteWalletBalanceClient,用于在配置启用时获取主站钱包余额。 更新 WalletBalanceController:根据主站 API 返回结果新增 main_balance 与 main_balance_formatted 字段。 在 lottery.php 中新增钱包余额 API 路径配置项。 增强 WalletBalanceTest,验证在配置主站 API 后可正确获取 main_balance。 --- .../Api/V1/Wallet/WalletBalanceController.php | 15 ++++- .../HttpMainSiteWalletBalanceClient.php | 61 +++++++++++++++++++ config/lottery.php | 1 + tests/Feature/WalletBalanceTest.php | 34 +++++++++++ 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 app/Services/Wallet/HttpMainSiteWalletBalanceClient.php diff --git a/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php b/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php index 85a1f03..9cd0780 100644 --- a/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php +++ b/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php @@ -11,6 +11,7 @@ use App\Support\CurrencyResolver; use Illuminate\Http\JsonResponse; use App\Support\CurrencyFormatter; use App\Http\Controllers\Controller; +use App\Services\Wallet\HttpMainSiteWalletBalanceClient; /** * 【玩家】查询彩票侧钱包余额。 @@ -22,12 +23,16 @@ use App\Http\Controllers\Controller; * - 币种:优先 Query `currency`,否则使用玩家 `default_currency`,再回退 `config('lottery.default_currency')` * - 若尚无 `player_wallets` 记录:按 `wallet_type=lottery` + 币种 **首次开立**一行(余额 0),便于新玩家直接进入查询 * - `available_balance`:`balance - frozen_balance`,表示当前可用于下注的整数最小货币单位(不为负) - * - `main_balance`:主站钱包余额占位,接入主站 API 后再返回实数;当前固定 `null` + * - `main_balance`:已配置 `MAIN_SITE_WALLET_API_URL` 时向主站查询;否则 `null` */ final class WalletBalanceController extends Controller { private const WALLET_TYPE_LOTTERY = 'lottery'; + public function __construct( + private readonly HttpMainSiteWalletBalanceClient $mainSiteBalanceClient, + ) {} + public function __invoke(Request $request): JsonResponse { $player = $request->lotteryPlayer(); @@ -56,13 +61,17 @@ final class WalletBalanceController extends Controller $frozen = (int) $wallet->frozen_balance; $available = max(0, $balance - $frozen); + $mainBalance = $this->mainSiteBalanceClient->fetch($player, $currencyCode); + return ApiResponse::success([ 'balance' => $balance, 'balance_formatted' => CurrencyFormatter::fromMinor($balance), 'available_balance' => $available, 'available_balance_formatted' => CurrencyFormatter::fromMinor($available), - 'main_balance' => null, - 'main_balance_formatted' => null, + 'main_balance' => $mainBalance, + 'main_balance_formatted' => $mainBalance !== null + ? CurrencyFormatter::fromMinor($mainBalance) + : null, 'currency_code' => $wallet->currency_code, 'wallet_type' => $wallet->wallet_type, 'frozen_balance' => $frozen, diff --git a/app/Services/Wallet/HttpMainSiteWalletBalanceClient.php b/app/Services/Wallet/HttpMainSiteWalletBalanceClient.php new file mode 100644 index 0000000..e69ba43 --- /dev/null +++ b/app/Services/Wallet/HttpMainSiteWalletBalanceClient.php @@ -0,0 +1,61 @@ + 'application/json']; + if (is_string($apiKey) && $apiKey !== '') { + $headers['Authorization'] = 'Bearer '.$apiKey; + } + + try { + $response = Http::withHeaders($headers) + ->timeout($timeout) + ->acceptJson() + ->get($url, [ + 'site_code' => $player->site_code, + 'site_player_id' => $player->site_player_id, + 'currency_code' => $currencyCode, + ]); + } catch (\Throwable) { + return null; + } + + if (! $response->successful()) { + return null; + } + + $payload = $response->json(); + if (! is_array($payload)) { + return null; + } + + $raw = data_get($payload, 'data.main_balance') + ?? data_get($payload, 'main_balance'); + + if (! is_numeric($raw)) { + return null; + } + + return max(0, (int) $raw); + } +} diff --git a/config/lottery.php b/config/lottery.php index 7b7e50a..519a397 100644 --- a/config/lottery.php +++ b/config/lottery.php @@ -37,6 +37,7 @@ return [ /** 主站钱包 HTTP 相对路径(拼接在 wallet_api_url 后);Stub 模式下忽略 */ 'wallet_debit_path' => env('MAIN_SITE_WALLET_DEBIT_PATH', '/wallet/debit-for-lottery'), 'wallet_credit_path' => env('MAIN_SITE_WALLET_CREDIT_PATH', '/wallet/credit-from-lottery'), + 'wallet_balance_path' => env('MAIN_SITE_WALLET_BALANCE_PATH', '/wallet/balance'), ], /* diff --git a/tests/Feature/WalletBalanceTest.php b/tests/Feature/WalletBalanceTest.php index 1d2a82f..3c459fb 100644 --- a/tests/Feature/WalletBalanceTest.php +++ b/tests/Feature/WalletBalanceTest.php @@ -5,11 +5,13 @@ use App\Lottery\ErrorCode; use App\Models\PlayerWallet; use Database\Seeders\CurrencySeeder; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\Http; uses(RefreshDatabase::class); beforeEach(function (): void { $this->seed(CurrencySeeder::class); + config(['lottery.main_site.wallet_api_url' => null]); }); test('wallet balance creates lottery wallet row and returns zeros', function () { @@ -55,6 +57,38 @@ test('wallet balance rejects illegal currency query', function () { ->assertJsonPath('code', ErrorCode::WalletInvalidCurrency->value); }); +test('wallet balance returns main_balance when main site wallet api is configured', function () { + config([ + 'lottery.main_site.wallet_api_url' => 'http://fake-main.test', + 'lottery.main_site.wallet_balance_path' => '/wallet/balance', + ]); + + Http::preventStrayRequests(); + Http::fake([ + 'http://fake-main.test/wallet/balance*' => Http::response([ + 'success' => true, + 'data' => [ + 'main_balance' => 499_900, + 'currency_code' => 'NPR', + ], + ], 200), + ]); + + $player = Player::query()->create([ + 'site_code' => 'main-site', + 'site_player_id' => '10003', + 'username' => null, + 'nickname' => null, + 'default_currency' => 'NPR', + 'status' => 0, + ]); + + $this->withHeader('Authorization', 'Bearer dev:'.$player->id) + ->getJson('/api/v1/wallet/balance') + ->assertOk() + ->assertJsonPath('data.main_balance', 499_900); +}); + test('wallet balance rejects currency that is not configured or disabled', function () { $player = Player::query()->create([ 'site_code' => 'test',