unwrap($row); } public function find(int $id): ?array { $row = DB::selectOne( 'select id, uuid, first_name, last_name, display_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, last_login_at, last_login_provider, password, locale, totp_secret, theme, primary_tenant_id, current_tenant_id, created_by, modified_by, created, modified, active, authz_version from users where id = ? limit 1', (string) $id ); return $this->unwrap($row); } public function findByUuid(string $uuid): ?array { $row = DB::selectOne( 'select id, uuid, first_name, last_name, display_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, last_login_at, last_login_provider, password, locale, totp_secret, theme, primary_tenant_id, current_tenant_id, created_by, modified_by, created, modified, active, authz_version, active_changed_at, active_changed_by from users where uuid = ? limit 1', $uuid ); return $this->unwrap($row); } public function findByEmail(string $email): ?array { $row = DB::selectOne( 'select id, uuid, first_name, last_name, display_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, last_login_at, last_login_provider, password, locale, totp_secret, theme, primary_tenant_id, current_tenant_id, created_by, modified_by, created, modified, active, authz_version, active_changed_at, active_changed_by from users where email = ? limit 1', $email ); return $this->unwrap($row); } public function listPrivilegedUserIdsByPermissionKeys(array $permissionKeys): array { $keys = array_values(array_unique(array_filter(array_map( static fn ($key) => trim((string) $key), $permissionKeys )))); if (!$keys) { return []; } $placeholders = implode(',', array_fill(0, count($keys), '?')); $rows = DB::select( 'select distinct ur.user_id from user_roles ur ' . 'join roles r on r.id = ur.role_id and r.active = 1 ' . 'join role_permissions rp on rp.role_id = ur.role_id ' . 'join permissions p on p.id = rp.permission_id and p.active = 1 ' . "where p.`key` in ($placeholders)", ...$keys ); if (!is_array($rows)) { return []; } $ids = []; foreach ($rows as $row) { $data = $row['ur'] ?? $row['user_roles'] ?? $row; $userId = (int) ($data['user_id'] ?? $row['user_id'] ?? 0); if ($userId > 0) { $ids[] = $userId; } } return array_values(array_unique($ids)); } public function listIdsForAutoDeactivate(int $days, array $excludedUserIds, int $limit = 500): array { if ($days <= 0 || $limit <= 0) { return []; } $excluded = array_values(array_unique(array_filter(array_map('intval', $excludedUserIds), static fn ($id) => $id > 0))); $query = 'select id from users where active = 1 and coalesce(last_login_at, created) <= DATE_SUB(UTC_TIMESTAMP(), INTERVAL ' . (int) $days . ' DAY)'; $params = []; if ($excluded) { $placeholders = implode(',', array_fill(0, count($excluded), '?')); $query .= " and id not in ($placeholders)"; $params = array_map('strval', $excluded); } $query .= ' order by coalesce(last_login_at, created) asc limit ?'; $params[] = (string) $limit; $rows = DB::select($query, ...$params); if (!is_array($rows)) { return []; } return $this->extractIdList($rows); } public function listIdsForAutoDelete(int $days, array $excludedUserIds, int $limit = 500): array { if ($days <= 0 || $limit <= 0) { return []; } $excluded = array_values(array_unique(array_filter(array_map('intval', $excludedUserIds), static fn ($id) => $id > 0))); $query = 'select id from users where active = 0 and active_changed_at is not null and active_changed_at <= DATE_SUB(UTC_TIMESTAMP(), INTERVAL ' . (int) $days . ' DAY)'; $params = []; if ($excluded) { $placeholders = implode(',', array_fill(0, count($excluded), '?')); $query .= " and id not in ($placeholders)"; $params = array_map('strval', $excluded); } $query .= ' order by active_changed_at asc limit ?'; $params[] = (string) $limit; $rows = DB::select($query, ...$params); if (!is_array($rows)) { return []; } return $this->extractIdList($rows); } private function unwrap(?array $row): ?array { if (!$row) { return null; } return $row['users'] ?? null; } private function extractIdList(array $rows): array { $ids = []; foreach ($rows as $row) { $data = $row['users'] ?? $row; $id = (int) ($data['id'] ?? $row['id'] ?? 0); if ($id > 0) { $ids[] = $id; } } return array_values(array_unique($ids)); } }