php-tenant/Services/UserStatsService.php
Snider d0ad2737cb refactor: rename namespace from Core\Mod\Tenant to Core\Tenant
Simplifies the namespace hierarchy by removing the intermediate Mod
segment. Updates all 118 files including models, services, controllers,
middleware, tests, and composer.json autoload configuration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 16:30:46 +00:00

284 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Tenant\Services;
use Core\Tenant\Enums\UserTier;
use Core\Tenant\Models\User;
use Illuminate\Support\Facades\Cache;
class UserStatsService
{
/**
* Compute and cache stats for a user.
*/
public function computeStats(User $user): array
{
$tier = $user->getTier();
$stats = [
'quotas' => $this->computeQuotas($user, $tier),
'services' => $this->computeServiceStats($user),
'activity' => $this->getRecentActivity($user),
];
// Save to user record
$user->cached_stats = $stats;
$user->stats_computed_at = now();
$user->save();
return $stats;
}
/**
* Get cached stats or compute fresh if stale (> 5 minutes).
*/
public function getStats(User $user): array
{
// Return cached if fresh (computed within last 5 minutes)
if ($user->stats_computed_at && $user->stats_computed_at->gt(now()->subMinutes(5))) {
return $user->cached_stats ?? $this->getDefaultStats($user);
}
// For page loads, return cached data immediately and queue refresh
if ($user->cached_stats) {
// Queue background refresh
dispatch(new \Core\Tenant\Jobs\ComputeUserStats($user->id))->onQueue('stats');
return $user->cached_stats;
}
// No cached data - compute synchronously (first time only)
return $this->computeStats($user);
}
/**
* Get default stats structure for a user tier.
*/
public function getDefaultStats(User $user): array
{
$tier = $user->getTier();
return [
'quotas' => $this->getTierLimits($tier),
'services' => $this->getDefaultServiceStats(),
'activity' => [],
];
}
/**
* Compute actual quota usage for user.
*/
protected function computeQuotas(User $user, UserTier $tier): array
{
$limits = $this->getTierLimits($tier);
// Compute actual usage
// Host Hub workspaces the user has access to (via pivot table)
$workspaceCount = $user->hostWorkspaces()->count();
$limits['workspaces']['used'] = $workspaceCount;
// Social accounts across all workspaces
// TODO: Implement when social accounts are linked
// $socialAccountCount = ...
// Scheduled posts
// TODO: Implement when scheduled posts are linked
// $scheduledPostCount = ...
// Storage usage
// TODO: Implement when media storage tracking is added
// $storageUsed = ...
return $limits;
}
/**
* Get tier limits configuration.
*/
protected function getTierLimits(UserTier $tier): array
{
return match ($tier) {
UserTier::HADES => [
'workspaces' => ['used' => 0, 'limit' => null, 'label' => 'Workspaces'],
'social_accounts' => ['used' => 0, 'limit' => null, 'label' => 'Social Accounts'],
'scheduled_posts' => ['used' => 0, 'limit' => null, 'label' => 'Scheduled Posts'],
'storage' => ['used' => 0, 'limit' => null, 'label' => 'Storage (GB)'],
],
UserTier::APOLLO => [
'workspaces' => ['used' => 0, 'limit' => 5, 'label' => 'Workspaces'],
'social_accounts' => ['used' => 0, 'limit' => 25, 'label' => 'Social Accounts'],
'scheduled_posts' => ['used' => 0, 'limit' => 500, 'label' => 'Scheduled Posts'],
'storage' => ['used' => 0, 'limit' => 10, 'label' => 'Storage (GB)'],
],
default => [
'workspaces' => ['used' => 0, 'limit' => 1, 'label' => 'Workspaces'],
'social_accounts' => ['used' => 0, 'limit' => 5, 'label' => 'Social Accounts'],
'scheduled_posts' => ['used' => 0, 'limit' => 50, 'label' => 'Scheduled Posts'],
'storage' => ['used' => 0, 'limit' => 1, 'label' => 'Storage (GB)'],
],
};
}
/**
* Compute service stats for user.
*/
protected function computeServiceStats(User $user): array
{
$services = [
[
'name' => 'SocialHost',
'icon' => 'fa-share-nodes',
'color' => 'bg-blue-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
[
'name' => 'BioHost',
'icon' => 'fa-id-card',
'color' => 'bg-violet-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
[
'name' => 'AnalyticsHost',
'icon' => 'fa-chart-line',
'color' => 'bg-green-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
[
'name' => 'TrustHost',
'icon' => 'fa-shield-check',
'color' => 'bg-amber-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
];
// Check for active Host Hub workspaces (via pivot table)
$workspaceCount = $user->hostWorkspaces()->count();
if ($workspaceCount > 0) {
// SocialHost - check for social accounts
// TODO: Check social accounts when integration is complete
$services[0]['status'] = 'active';
$services[0]['stat'] = $workspaceCount.' workspace(s)';
// BioHost - check for bio pages
// TODO: Check for bio pages when implemented
}
return $services;
}
/**
* Get default service stats.
*/
protected function getDefaultServiceStats(): array
{
return [
[
'name' => 'SocialHost',
'icon' => 'fa-share-nodes',
'color' => 'bg-blue-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
[
'name' => 'BioHost',
'icon' => 'fa-id-card',
'color' => 'bg-violet-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
[
'name' => 'AnalyticsHost',
'icon' => 'fa-chart-line',
'color' => 'bg-green-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
[
'name' => 'TrustHost',
'icon' => 'fa-shield-check',
'color' => 'bg-amber-500',
'status' => 'inactive',
'stat' => 'Not configured',
],
];
}
/**
* Get recent activity for user.
*/
protected function getRecentActivity(User $user): array
{
// TODO: Implement actual activity logging
// For now return empty - activities will be added when actions are performed
return [];
}
/**
* Get cached timezone list.
*/
public static function getTimezoneList(): array
{
return Cache::remember('timezone_list', 86400, function () {
$groups = [];
$timezones = \DateTimeZone::listIdentifiers(\DateTimeZone::ALL);
foreach ($timezones as $tz) {
$parts = explode('/', $tz, 2);
$group = $parts[0] ?? 'Other';
$label = $parts[1] ?? $tz;
if (! isset($groups[$group])) {
$groups[$group] = [];
}
$groups[$group][$tz] = str_replace('_', ' ', $label);
}
ksort($groups);
foreach ($groups as &$items) {
asort($items);
}
return $groups;
});
}
/**
* Get cached locale list.
*/
public static function getLocaleList(): array
{
return Cache::remember('locale_list', 86400, function () {
$locales = [
'en-GB' => 'English (UK)',
'en-US' => 'English (US)',
'es' => 'Español',
'fr' => 'Français',
'de' => 'Deutsch',
'it' => 'Italiano',
'pt' => 'Português',
'nl' => 'Nederlands',
'pl' => 'Polski',
'ru' => 'Русский',
'ja' => '日本語',
'zh' => '中文',
'ko' => '한국어',
'ar' => 'العربية',
];
$result = [];
foreach ($locales as $code => $name) {
$result[] = ['long' => $code, 'name' => $name];
}
return $result;
});
}
}