create(['username' => 'admin', 'password' => 'admin123']); TransferRecord::query()->create([ 'from_address' => 'TFrom1111111111111111111111111111', 'to_address' => 'TTo222222222222222222222222222222', 'chain' => 'tron', 'tx_hash' => 'abc123', 'amount' => '10', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'tg:1', 'status' => TransferRecord::STATUS_SUCCESS, 'error' => null, ]); $this->actingAs($admin, 'admin') ->get(route('admin.transfers.index')) ->assertOk() ->assertSee('交易记录') ->assertSee('时间范围') ->assertSee('成功归集金额') ->assertSee('手续费补足金额'); $this->actingAs($admin, 'admin') ->getJson(route('admin.transfers.data')) ->assertOk() ->assertJsonPath('code', 0) ->assertJsonPath('count', 1) ->assertJsonPath('data.0.asset', 'USDT') ->assertJsonPath('data.0.status', 'success') ->assertJsonPath('summary.totals.0.asset', 'USDT') ->assertJsonPath('summary.totals.0.amount', '10') ->assertJsonPath('summary.totals.1.asset', 'ETH') ->assertJsonPath('summary.totals.1.amount', '0'); } #[Test] public function transfer_summary_follows_filters_and_counts_success_only(): void { $admin = Admin::query()->create(['username' => 'admin-sum', 'password' => 'admin123']); TransferRecord::query()->create([ 'from_address' => 'TFromA', 'to_address' => 'TToA', 'chain' => 'tron', 'tx_hash' => 'ok-usdt', 'amount' => '12.5', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'auto', 'status' => TransferRecord::STATUS_SUCCESS, ]); TransferRecord::query()->create([ 'from_address' => '0xFromB', 'to_address' => '0xToB', 'chain' => 'eth', 'tx_hash' => 'ok-eth', 'amount' => '0.75', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'ETH', 'operator' => 'admin', 'status' => TransferRecord::STATUS_SUCCESS, ]); TransferRecord::query()->create([ 'from_address' => 'TFromC', 'to_address' => 'TToC', 'chain' => 'tron', 'tx_hash' => 'fail-usdt', 'amount' => '99', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'auto', 'status' => TransferRecord::STATUS_FAILED, ]); TransferRecord::query()->create([ 'from_address' => 'TFeeFrom', 'to_address' => 'TFeeTo', 'chain' => 'tron', 'tx_hash' => 'fee-ok', 'amount' => '20', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'TRX', 'operator' => 'fee-topup:auto', 'status' => TransferRecord::STATUS_SUCCESS, ]); $this->actingAs($admin, 'admin') ->getJson(route('admin.transfers.data')) ->assertOk() ->assertJsonPath('summary.totals.0.asset', 'USDT') ->assertJsonPath('summary.totals.0.amount', '12.5') ->assertJsonPath('summary.totals.1.asset', 'ETH') ->assertJsonPath('summary.totals.1.amount', '0.75') ->assertJsonPath('summary.totals.2.asset', 'TRX') ->assertJsonPath('summary.totals.2.amount', '0') ->assertJsonPath('summary.fee_totals.2.asset', 'TRX') ->assertJsonPath('summary.fee_totals.2.amount', '20'); $this->actingAs($admin, 'admin') ->getJson(route('admin.transfers.data', ['asset' => 'USDT', 'status' => 'failed'])) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('summary.totals.0.amount', '12.5') ->assertJsonPath('summary.totals.1.amount', '0'); } #[Test] public function transfer_summary_respects_date_range(): void { $admin = Admin::query()->create(['username' => 'admin-range', 'password' => 'admin123']); $old = TransferRecord::query()->create([ 'from_address' => 'TOld', 'to_address' => 'TTo', 'chain' => 'tron', 'tx_hash' => 'old', 'amount' => '20', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'auto', 'status' => TransferRecord::STATUS_SUCCESS, ]); $old->created_at = '2026-01-01 12:00:00'; $old->save(); $fresh = TransferRecord::query()->create([ 'from_address' => 'TNew', 'to_address' => 'TTo', 'chain' => 'tron', 'tx_hash' => 'new', 'amount' => '3', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'auto', 'status' => TransferRecord::STATUS_SUCCESS, ]); $fresh->created_at = '2026-10-01 12:00:00'; $fresh->save(); $this->actingAs($admin, 'admin') ->getJson(route('admin.transfers.data', [ 'date_from' => '2026-10-01', 'date_to' => '2026-10-02', ])) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('data.0.tx_hash', 'new') ->assertJsonPath('summary.totals.0.amount', '3'); } #[Test] public function agent_only_sees_transfers_for_own_channel_addresses(): void { $agent = User::query()->create(['username' => 'agent-tx', 'password' => 'secret12', 'status' => 1]); $ch = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; Channel::query()->create(['channel_id' => $ch, 'user_id' => $agent->id, 'status' => 1]); $device = Device::query()->create(['device_id' => 'dev-tx-1', 'channel_id' => $ch]); WalletAddress::query()->create([ 'device_id' => $device->id, 'address' => 'TMine3333333333333333333333333333', 'chain_type' => 'TRON', 'source' => 'trust', ]); TransferRecord::query()->create([ 'from_address' => 'TMine3333333333333333333333333333', 'to_address' => 'TTo', 'chain' => 'tron', 'tx_hash' => 'mine', 'amount' => '1', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'tg:agent', 'status' => TransferRecord::STATUS_SUCCESS, ]); TransferRecord::query()->create([ 'from_address' => 'TOther444444444444444444444444444', 'to_address' => 'TTo', 'chain' => 'tron', 'tx_hash' => 'other', 'amount' => '2', 'type' => TransferRecord::TYPE_OUT, 'asset' => 'USDT', 'operator' => 'tg:other', 'status' => TransferRecord::STATUS_SUCCESS, ]); $this->actingAs($agent, 'agent') ->getJson(route('user.transfers.data')) ->assertOk() ->assertJsonPath('count', 1) ->assertJsonPath('data.0.tx_hash', 'mine'); } }