feat: xxbb
This commit is contained in:
@@ -30,7 +30,7 @@ class AdminUserController extends Controller
|
||||
}
|
||||
$q->orderBy($field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class AgentUserController extends Controller
|
||||
}
|
||||
$q->orderBy($field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class ChannelController extends Controller
|
||||
$q->where('status', (int) $status);
|
||||
}
|
||||
|
||||
$sortable = ['id', 'channel_id', 'channel_name', 'builder_type', 'status', 'user_id', 'created_at', 'updated_at'];
|
||||
$sortable = ['id', 'channel_id', 'builder_type', 'status', 'user_id', 'created_at', 'updated_at'];
|
||||
$field = (string) $request->query('field', 'id');
|
||||
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
|
||||
if (! in_array($field, $sortable, true)) {
|
||||
@@ -60,24 +60,29 @@ class ChannelController extends Controller
|
||||
}
|
||||
$q->orderBy($field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
$data = collect($paginator->items())->map(function (Channel $c) {
|
||||
return [
|
||||
$row = [
|
||||
'id' => $c->id,
|
||||
'channel_id' => $c->channel_id,
|
||||
'builder_type' => $c->builderType(),
|
||||
'channel_name' => $c->channel_name ?: '',
|
||||
'user_id' => (int) $c->user_id,
|
||||
'agent_username' => $c->agentLabel(),
|
||||
'remark' => $c->remark ?: '',
|
||||
'status' => (int) $c->status,
|
||||
'links' => $c->supportLinks(),
|
||||
'download_url' => null,
|
||||
'created_at' => optional($c->created_at)->format('Y-m-d H:i:s'),
|
||||
'updated_at' => optional($c->updated_at)->format('Y-m-d H:i:s'),
|
||||
];
|
||||
if ($c->isNewBuilder()) {
|
||||
$row['download_url'] = route($this->portal().'.channels.download', $c);
|
||||
}
|
||||
|
||||
return $row;
|
||||
})->values();
|
||||
|
||||
return response()->json([
|
||||
@@ -88,12 +93,17 @@ class ChannelController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function randomId()
|
||||
public function randomId(Request $request)
|
||||
{
|
||||
$builderType = strtolower(trim((string) $request->query('builder_type', Channel::BUILDER_OLD)));
|
||||
$channelId = $builderType === Channel::BUILDER_NEW
|
||||
? Channel::randomNewChannelId()
|
||||
: Channel::randomChannelId();
|
||||
|
||||
return response()->json([
|
||||
'code' => 0,
|
||||
'msg' => '',
|
||||
'data' => ['channel_id' => Channel::randomChannelId()],
|
||||
'data' => ['channel_id' => $channelId],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -101,8 +111,17 @@ class ChannelController extends Controller
|
||||
{
|
||||
abort_if($this->isAgentPortal(), 403);
|
||||
|
||||
$builderType = strtolower(trim((string) $request->input('builder_type', Channel::BUILDER_OLD)));
|
||||
if (! in_array($builderType, [Channel::BUILDER_OLD, Channel::BUILDER_NEW], true)) {
|
||||
$builderType = Channel::BUILDER_OLD;
|
||||
}
|
||||
|
||||
$channelIdRule = $builderType === Channel::BUILDER_NEW
|
||||
? ['required', 'string', 'size:'.Channel::NEW_CHANNEL_ID_LENGTH, 'regex:/^[A-Za-z0-9]+$/']
|
||||
: ['required', 'string', 'size:32', 'regex:/^[a-z0-9]+$/', Rule::unique('channels', 'channel_id')];
|
||||
|
||||
$data = $request->validate([
|
||||
'channel_id' => ['required', 'string', 'size:32', 'regex:/^[a-z0-9]+$/', Rule::unique('channels', 'channel_id')],
|
||||
'channel_id' => $channelIdRule,
|
||||
'builder_type' => ['nullable', 'string', Rule::in([Channel::BUILDER_OLD, Channel::BUILDER_NEW])],
|
||||
'user_id' => ['nullable', 'integer', 'min:0'],
|
||||
'support_template' => ['nullable', 'string', Rule::in(ChannelProjectService::SUPPORT_TEMPLATES)],
|
||||
@@ -112,13 +131,23 @@ class ChannelController extends Controller
|
||||
'status' => ['nullable', 'integer', Rule::in([0, 1])],
|
||||
]);
|
||||
|
||||
if ($builderType === Channel::BUILDER_NEW) {
|
||||
$normalized = Channel::normalizeNewChannelId((string) $data['channel_id']);
|
||||
if ($normalized === null) {
|
||||
throw ValidationException::withMessages(['channel_id' => '新版渠道 ID 必须是 8 位字母或数字']);
|
||||
}
|
||||
$data['channel_id'] = $normalized;
|
||||
if (Channel::query()->whereRaw('upper(channel_id) = ?', [$normalized])->exists()) {
|
||||
throw ValidationException::withMessages(['channel_id' => '渠道 ID 已存在']);
|
||||
}
|
||||
}
|
||||
|
||||
$userId = (int) ($data['user_id'] ?? Channel::OFFICIAL_USER_ID);
|
||||
if ($userId > 0 && ! User::query()->whereKey($userId)->exists()) {
|
||||
throw ValidationException::withMessages(['user_id' => '代理用户不存在']);
|
||||
}
|
||||
|
||||
$builderType = (string) ($data['builder_type'] ?? Channel::BUILDER_OLD);
|
||||
$channelName = null;
|
||||
$builderType = (string) ($data['builder_type'] ?? $builderType);
|
||||
$supportTemplate = (string) ($data['support_template'] ?? ChannelProjectService::DEFAULT_SUPPORT_TEMPLATE);
|
||||
$this->assertAgentChannelQuota($userId);
|
||||
|
||||
@@ -129,9 +158,7 @@ class ChannelController extends Controller
|
||||
$data['deployment_seed'] ?? null,
|
||||
$data['reporting_seed'] ?? null,
|
||||
$builderType,
|
||||
$channelName,
|
||||
);
|
||||
$channelName = $build['channel_name'] ?? null;
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
@@ -140,7 +167,7 @@ class ChannelController extends Controller
|
||||
}
|
||||
|
||||
try {
|
||||
$channel = DB::transaction(function () use ($data, $userId, $builderType, $channelName) {
|
||||
$channel = DB::transaction(function () use ($data, $userId, $builderType) {
|
||||
if ($userId > 0) {
|
||||
$userExists = User::query()->lockForUpdate()->whereKey($userId)->exists();
|
||||
if (! $userExists) {
|
||||
@@ -153,7 +180,6 @@ class ChannelController extends Controller
|
||||
return Channel::query()->create([
|
||||
'channel_id' => $data['channel_id'],
|
||||
'builder_type' => $builderType,
|
||||
'channel_name' => $channelName,
|
||||
'user_id' => $userId,
|
||||
'domains' => [],
|
||||
'remark' => $data['remark'] ?? null,
|
||||
@@ -161,7 +187,7 @@ class ChannelController extends Controller
|
||||
]);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
$this->compensateBuildUnlessChannelExists($projects, $data['channel_id'], $builderType, $channelName);
|
||||
$this->compensateBuildUnlessChannelExists($projects, $data['channel_id'], $builderType);
|
||||
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
@@ -175,7 +201,6 @@ class ChannelController extends Controller
|
||||
'data' => [
|
||||
'id' => $channel->id,
|
||||
'builder_type' => $channel->builderType(),
|
||||
'channel_name' => $channel->channel_name,
|
||||
'links' => $channel->supportLinks(),
|
||||
'seeds' => $build['seeds'],
|
||||
'domains' => $build['domains'],
|
||||
@@ -184,6 +209,10 @@ class ChannelController extends Controller
|
||||
'support_path' => $build['support_path'] ?? $channel->landingPath(),
|
||||
'weifile_path' => $build['weifile_path'] ?? null,
|
||||
'daily_path' => $build['daily_path'] ?? '',
|
||||
'zip_path' => $build['zip_path'] ?? null,
|
||||
'download_url' => $channel->isNewBuilder()
|
||||
? route('admin.channels.download', $channel)
|
||||
: null,
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -236,6 +265,19 @@ class ChannelController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function download(Channel $channel, ChannelProjectService $projects)
|
||||
{
|
||||
$this->authorizeChannel($channel);
|
||||
abort_unless($channel->isNewBuilder(), 404);
|
||||
|
||||
$path = $projects->newChannelZipPath($channel->channel_id);
|
||||
abort_unless(is_file($path), 404, '安装包不存在,请重新创建渠道');
|
||||
|
||||
return response()->download($path, $channel->channel_id.'.zip', [
|
||||
'Content-Type' => 'application/zip',
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Channel $channel, ChannelProjectService $projects)
|
||||
{
|
||||
abort_if($this->isAgentPortal(), 403);
|
||||
@@ -243,11 +285,10 @@ class ChannelController extends Controller
|
||||
|
||||
$channelId = $channel->channel_id;
|
||||
$builderType = $channel->builderType();
|
||||
$channelName = $channel->channel_name;
|
||||
try {
|
||||
// Delete remotely first: a failed remote delete leaves the DB row available
|
||||
// for a safe retry instead of orphaning an unreachable static project.
|
||||
$projects->deleteWebTree($channelId, $builderType, $channelName);
|
||||
$projects->deleteWebTree($channelId, $builderType);
|
||||
DB::transaction(static fn () => $channel->delete());
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json([
|
||||
@@ -288,7 +329,6 @@ class ChannelController extends Controller
|
||||
ChannelProjectService $projects,
|
||||
string $channelId,
|
||||
string $builderType = Channel::BUILDER_OLD,
|
||||
?string $channelName = null,
|
||||
): void {
|
||||
try {
|
||||
// A concurrent request may have won the unique channel_id insert. Its
|
||||
@@ -306,12 +346,11 @@ class ChannelController extends Controller
|
||||
}
|
||||
|
||||
try {
|
||||
$projects->deleteWebTree($channelId, $builderType, $channelName);
|
||||
$projects->deleteWebTree($channelId, $builderType);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Failed to compensate channel build', [
|
||||
'channel_id' => $channelId,
|
||||
'builder_type' => $builderType,
|
||||
'channel_name' => $channelName,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,11 @@ use App\Models\User;
|
||||
use App\Models\WalletAddress;
|
||||
use App\Models\WalletMnemonic;
|
||||
use App\Support\AgentScope;
|
||||
use App\Services\Tokenview\TokenviewMonitorService;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DeviceController extends Controller
|
||||
@@ -38,7 +41,7 @@ class DeviceController extends Controller
|
||||
$filters = $this->filtersFrom($request);
|
||||
$q = $this->filteredQuery($filters);
|
||||
|
||||
$sortable = ['id', 'device_id', 'channel_id', 'device_model', 'ios_version', 'ip', 'created_at', 'updated_at'];
|
||||
$sortable = ['id', 'device_id', 'channel_id', 'device_model', 'ios_version', 'ip', 'has_wallet', 'created_at', 'updated_at'];
|
||||
$field = (string) $request->query('field', 'updated_at');
|
||||
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
|
||||
if (! in_array($field, $sortable, true)) {
|
||||
@@ -46,7 +49,7 @@ class DeviceController extends Controller
|
||||
}
|
||||
$q->orderBy('devices.'.$field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['devices.*'], 'page', $page);
|
||||
|
||||
@@ -56,13 +59,17 @@ class DeviceController extends Controller
|
||||
'id' => $d->id,
|
||||
'device_id' => $d->device_id,
|
||||
'channel_id' => $d->channel_id ?: '',
|
||||
'source_domain' => $d->source_domain ?: '',
|
||||
'device_model' => $d->device_model ?: '',
|
||||
'ios_version' => $d->ios_version ?: '',
|
||||
'ip' => $d->ip ?: '',
|
||||
'has_wallet' => (int) $d->has_wallet,
|
||||
'wallet_names' => $d->walletNameList(),
|
||||
'album_storage' => $d->albumStorageEnabled() ? 1 : 0,
|
||||
'created_at' => optional($d->created_at)->format('Y-m-d H:i:s'),
|
||||
'updated_at' => optional($d->updated_at)->format('Y-m-d H:i:s'),
|
||||
'detail_url' => route($portal.'.devices.show', $d),
|
||||
'destroy_url' => route($portal.'.devices.destroy', $d),
|
||||
];
|
||||
})->values();
|
||||
|
||||
@@ -116,7 +123,7 @@ class DeviceController extends Controller
|
||||
$this->authorizeDevice($device);
|
||||
|
||||
$tab = (string) $request->query('tab', 'wallets');
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
|
||||
$field = (string) $request->query('field', 'id');
|
||||
@@ -169,9 +176,60 @@ class DeviceController extends Controller
|
||||
{
|
||||
$this->authorizeDevice($device);
|
||||
|
||||
$photos = $device->photos()->get(['id', 'path']);
|
||||
$deletedFiles = $this->deletePhotoFiles($device);
|
||||
|
||||
$deletedRows = $device->photos()->delete();
|
||||
$this->deleteStorageDir('c2/photos/'.$device->device_id);
|
||||
|
||||
return response()->json([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'data' => [
|
||||
'deleted_rows' => (int) $deletedRows,
|
||||
'deleted_files' => $deletedFiles,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Device $device)
|
||||
{
|
||||
$this->authorizeDevice($device);
|
||||
|
||||
$listUrl = route($this->portal().'.devices.index');
|
||||
$this->purgeDevice($device);
|
||||
|
||||
return response()->json([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'data' => [
|
||||
'list_url' => $listUrl,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function purgeDevice(Device $device): void
|
||||
{
|
||||
$this->deletePhotoFiles($device);
|
||||
$this->deleteStorageDir('c2/photos/'.$device->device_id);
|
||||
$this->deleteStorageDir('c2/check/'.$device->device_id);
|
||||
$this->unmonitorAddresses($device);
|
||||
|
||||
DB::transaction(function () use ($device) {
|
||||
$device->apps()->delete();
|
||||
$device->events()->delete();
|
||||
$device->photos()->delete();
|
||||
$device->notes()->delete();
|
||||
$device->addresses()->delete();
|
||||
$device->mnemonics()->delete();
|
||||
$device->keystores()->delete();
|
||||
$device->delete();
|
||||
});
|
||||
}
|
||||
|
||||
private function deletePhotoFiles(Device $device): int
|
||||
{
|
||||
$deletedFiles = 0;
|
||||
foreach ($photos as $photo) {
|
||||
foreach ($device->photos()->get(['id', 'path']) as $photo) {
|
||||
$path = trim((string) ($photo->path ?? ''));
|
||||
if ($path === '') {
|
||||
continue;
|
||||
@@ -182,29 +240,45 @@ class DeviceController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$deletedRows = $device->photos()->delete();
|
||||
return $deletedFiles;
|
||||
}
|
||||
|
||||
$dir = 'c2/photos/'.$device->device_id;
|
||||
private function deleteStorageDir(string $dir): void
|
||||
{
|
||||
try {
|
||||
if (Storage::disk('local')->directoryExists($dir)) {
|
||||
Storage::disk('local')->deleteDirectory($dir);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// older flysystem without directoryExists — best-effort wipe
|
||||
try {
|
||||
Storage::disk('local')->deleteDirectory($dir);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'data' => [
|
||||
'deleted_rows' => (int) $deletedRows,
|
||||
'deleted_files' => $deletedFiles,
|
||||
],
|
||||
]);
|
||||
private function unmonitorAddresses(Device $device): void
|
||||
{
|
||||
$addresses = $device->addresses()->where('monitor', 1)->get();
|
||||
if ($addresses->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$svc = app(TokenviewMonitorService::class);
|
||||
} catch (\Throwable) {
|
||||
return;
|
||||
}
|
||||
foreach ($addresses as $address) {
|
||||
try {
|
||||
$address->monitor = 0;
|
||||
$svc->syncMonitor($address);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('tokenview unmonitor on device delete failed: '.$e->getMessage(), [
|
||||
'device_id' => $device->id,
|
||||
'address_id' => $address->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function authorizeDevice(Device $device): void
|
||||
@@ -393,10 +467,16 @@ class DeviceController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, agent_user_id: ?int}
|
||||
* @return array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int}
|
||||
*/
|
||||
private function filtersFrom(Request $request): array
|
||||
{
|
||||
$hasWallet = $request->query('has_wallet');
|
||||
$hasWalletInt = null;
|
||||
if (in_array((string) $hasWallet, ['0', '1', '2'], true)) {
|
||||
$hasWalletInt = (int) $hasWallet;
|
||||
}
|
||||
|
||||
return [
|
||||
'device_key' => trim((string) $request->query('device_key', '')),
|
||||
'channel_id' => trim((string) $request->query('channel_id', '')),
|
||||
@@ -405,12 +485,13 @@ class DeviceController extends Controller
|
||||
'ios' => trim((string) $request->query('ios', '')),
|
||||
'installed_from' => trim((string) $request->query('installed_from', '')),
|
||||
'installed_to' => trim((string) $request->query('installed_to', '')),
|
||||
'has_wallet' => $hasWalletInt,
|
||||
'agent_user_id' => AgentScope::parseAgentUserIdFilter($request->query('agent_user_id')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, agent_user_id: ?int} $filters
|
||||
* @param array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int} $filters
|
||||
*/
|
||||
private function filteredQuery(array $filters): Builder
|
||||
{
|
||||
@@ -438,6 +519,9 @@ class DeviceController extends Controller
|
||||
if ($filters['installed_to'] !== '' && preg_match('/^\d{4}-\d{2}-\d{2}/', $filters['installed_to'])) {
|
||||
$q->whereDate('devices.created_at', '<=', substr($filters['installed_to'], 0, 10));
|
||||
}
|
||||
if ($filters['has_wallet'] !== null) {
|
||||
$q->where('devices.has_wallet', $filters['has_wallet']);
|
||||
}
|
||||
if (! $this->isAgentPortal()) {
|
||||
AgentScope::applyAgentUserFilter($q, $filters['agent_user_id']);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class MnemonicController extends Controller
|
||||
}
|
||||
$q->orderBy('wallet_mnemonics.'.$field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class NoteController extends Controller
|
||||
}
|
||||
$q->orderBy('notes.'.$field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class PageVisitController extends Controller
|
||||
->forPage($page, $limit)
|
||||
->get([
|
||||
'id', 'channel_id', 'client_uid', 'os', 'os_version',
|
||||
'browser', 'browser_version', 'user_agent', 'ip', 'path', 'referer', 'created_at',
|
||||
'browser', 'browser_version', 'user_agent', 'ip', 'domain', 'referer', 'created_at',
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
@@ -54,7 +54,7 @@ class PageVisitController extends Controller
|
||||
'browser_version' => $v->browser_version ?: '',
|
||||
'user_agent' => $v->user_agent ?: '',
|
||||
'ip' => $v->ip ?: '',
|
||||
'path' => $v->path ?: '',
|
||||
'domain' => $v->domain ?: '',
|
||||
'referer' => $v->referer ?: '',
|
||||
'created_at' => optional($v->created_at)?->toDateTimeString(),
|
||||
])->values(),
|
||||
@@ -71,6 +71,11 @@ class PageVisitController extends Controller
|
||||
->selectRaw('COUNT(*) as pv')
|
||||
->selectRaw('COUNT(DISTINCT client_uid) as uv')
|
||||
->groupBy('os', 'os_version'),
|
||||
'domain' => $base
|
||||
->select('domain')
|
||||
->selectRaw('COUNT(*) as pv')
|
||||
->selectRaw('COUNT(DISTINCT client_uid) as uv')
|
||||
->groupBy('domain'),
|
||||
'browser' => $base
|
||||
->select('browser')
|
||||
->selectRaw('COUNT(*) as pv')
|
||||
@@ -95,6 +100,7 @@ class PageVisitController extends Controller
|
||||
->map(static function ($row) use ($group) {
|
||||
$label = match ($group) {
|
||||
'os_version' => trim(((string) ($row->os ?? '')).' '.((string) ($row->os_version ?? ''))),
|
||||
'domain' => (string) ($row->domain ?? ''),
|
||||
'browser' => (string) ($row->browser ?? ''),
|
||||
'browser_version' => trim(((string) ($row->browser ?? '')).' '.((string) ($row->browser_version ?? ''))),
|
||||
default => (string) ($row->os ?? ''),
|
||||
@@ -151,6 +157,10 @@ class PageVisitController extends Controller
|
||||
if ($browserVersion !== '') {
|
||||
$q->where('browser_version', $browserVersion);
|
||||
}
|
||||
$domain = trim((string) $request->query('domain', ''));
|
||||
if ($domain !== '') {
|
||||
$q->where('domain', 'like', '%'.$domain.'%');
|
||||
}
|
||||
$referer = trim((string) $request->query('referer', ''));
|
||||
if ($referer !== '') {
|
||||
$q->where('referer', 'like', '%'.$referer.'%');
|
||||
|
||||
@@ -38,7 +38,7 @@ class PhotoController extends Controller
|
||||
}
|
||||
$q->orderBy('photos.'.$field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class TransferRecordController extends Controller
|
||||
}
|
||||
$q->orderBy('transfer_records.'.$field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['transfer_records.*'], 'page', $page);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class WalletAddressController extends Controller
|
||||
}
|
||||
$q->orderBy('wallet_addresses.'.$field, $order);
|
||||
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 15)));
|
||||
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user