feat: new channel

This commit is contained in:
hashbro
2026-08-15 03:12:09 +08:00
parent 7c0066562f
commit 74067e7b54
9 changed files with 285 additions and 46 deletions
+68 -13
View File
@@ -17,6 +17,9 @@ class DashboardStatsService
/**
* @param array{
* range?: string,
* date_from?: string,
* date_to?: string,
* date_range?: string,
* channel_id?: string,
* channel_exact?: bool,
* agent_user_id?: ?int,
@@ -44,8 +47,8 @@ class DashboardStatsService
*/
public function collect(array $filters = []): array
{
$range = (string) ($filters['range'] ?? '30d');
[$from, $to] = $this->rangeBounds($range);
[$from, $to] = $this->resolveBounds($filters);
$rangeLabel = $from->toDateString().' ~ '.$to->toDateString();
$channelId = trim((string) ($filters['channel_id'] ?? ''));
$channelExact = (bool) ($filters['channel_exact'] ?? false);
@@ -68,10 +71,13 @@ class DashboardStatsService
}
}
$total = (clone $base)->count();
$walletCount = (clone $base)->where('has_wallet', Device::WALLET_YES)->count();
$newCount = (clone $base)->whereBetween('created_at', [$from, $to])->count();
// Period cohort: devices installed (created) in the selected range.
$createdInRange = (clone $base)->whereBetween('created_at', [$from, $to]);
$total = (clone $createdInRange)->count();
$walletCount = (clone $createdInRange)->where('has_wallet', Device::WALLET_YES)->count();
$newCount = $total;
$activeCount = (clone $base)->whereBetween('updated_at', [$from, $to])->count();
$mnemonicCount = $this->countDeviceChildren(
WalletMnemonic::query(),
'wallet_mnemonics',
@@ -79,6 +85,8 @@ class DashboardStatsService
$agentUserId,
$channelId,
$channelExact,
$from,
$to,
);
$addressCount = $this->countDeviceChildren(
WalletAddress::query(),
@@ -87,8 +95,10 @@ class DashboardStatsService
$agentUserId,
$channelId,
$channelExact,
$from,
$to,
);
$balances = $this->sumBalances($agent, $agentUserId, $channelId, $channelExact);
$balances = $this->sumBalances($agent, $agentUserId, $channelId, $channelExact, $from, $to);
$visits = PageVisit::query();
AgentScope::applyChannelIdScope($visits, $agent);
@@ -143,7 +153,7 @@ class DashboardStatsService
->values()
->all();
$byDeviceIosVersion = (clone $base)
$byDeviceIosVersion = (clone $createdInRange)
->select('ios_version')
->selectRaw('COUNT(*) as cnt')
->groupBy('ios_version')
@@ -177,12 +187,49 @@ class DashboardStatsService
'by_os' => $byOs,
'by_ios_version' => $byIosVersion,
'by_device_ios_version' => $byDeviceIosVersion,
'range' => $range,
'range' => $rangeLabel,
'from' => $from->toDateTimeString(),
'to' => $to->toDateTimeString(),
'date_from' => $from->toDateString(),
'date_to' => $to->toDateString(),
];
}
/**
* @param array{range?: string, date_from?: string, date_to?: string, date_range?: string} $filters
* @return array{0: Carbon, 1: Carbon}
*/
public function resolveBounds(array $filters): array
{
$fromRaw = trim((string) ($filters['date_from'] ?? ''));
$toRaw = trim((string) ($filters['date_to'] ?? ''));
$rangeField = trim((string) ($filters['date_range'] ?? ''));
if ($fromRaw === '' && $toRaw === '' && $rangeField !== '') {
if (preg_match('/^(\d{4}-\d{2}-\d{2})\s*~\s*(\d{4}-\d{2}-\d{2})$/', $rangeField, $m)
|| preg_match('/^(\d{4}-\d{2}-\d{2})\s*-\s*(\d{4}-\d{2}-\d{2})$/', $rangeField, $m)) {
$fromRaw = $m[1];
$toRaw = $m[2];
}
}
if ($fromRaw !== '' && $toRaw !== '') {
try {
$from = Carbon::parse($fromRaw)->startOfDay();
$to = Carbon::parse($toRaw)->endOfDay();
} catch (\Throwable) {
return $this->rangeBounds((string) ($filters['range'] ?? '30d'));
}
if ($from->gt($to)) {
[$from, $to] = [$to->copy()->startOfDay(), $from->copy()->endOfDay()];
}
return [$from, $to];
}
return $this->rangeBounds((string) ($filters['range'] ?? '30d'));
}
/**
* Join a device-child table and apply the same channel / agent scope as device totals.
*/
@@ -217,19 +264,27 @@ class DashboardStatsService
?int $agentUserId,
string $channelId,
bool $channelExact,
Carbon $from,
Carbon $to,
): int {
return $this->scopedDeviceChildren($query, $table, $agent, $agentUserId, $channelId, $channelExact)
->whereBetween($table.'.created_at', [$from, $to])
->count($table.'.id');
}
/**
* Sum coin columns across wallet_addresses for devices in the same scope as dashboard totals.
* Not limited by the time range (current holdings).
* Sum coin columns for addresses created in the selected range.
*
* @return array{usdt: string, trx: string, eth: string, btc: string, bnb: string}
*/
private function sumBalances(?User $agent, ?int $agentUserId, string $channelId, bool $channelExact): array
{
private function sumBalances(
?User $agent,
?int $agentUserId,
string $channelId,
bool $channelExact,
Carbon $from,
Carbon $to,
): array {
$q = $this->scopedDeviceChildren(
WalletAddress::query(),
'wallet_addresses',
@@ -237,7 +292,7 @@ class DashboardStatsService
$agentUserId,
$channelId,
$channelExact,
);
)->whereBetween('wallet_addresses.created_at', [$from, $to]);
$selects = [];
foreach (WalletAddress::COIN_COLUMNS as $col) {