Files
nabeh/backend/app/Models/CompanySubscriptionUsage.php
2026-05-23 00:20:07 +03:00

141 lines
4.6 KiB
PHP

<?php
namespace App\Models;
use App\Core\Database;
use App\Core\Cache;
/**
* CompanySubscriptionUsage Model
* Tracks API usage stats per company dynamically linked to active billing cycles.
*/
class CompanySubscriptionUsage extends BaseModel
{
protected static string $table = 'company_subscription_usage';
/**
* Get or initialize usage record for the active billing cycle of a company
*/
public static function getOrCreateCurrentUsage(int $companyId, array $activeSubscription): array
{
$billingStart = date('Y-m-d', strtotime($activeSubscription['starts_at']));
$billingEnd = date('Y-m-d', strtotime($activeSubscription['ends_at']));
$cacheKey = "company_usage_{$companyId}_{$billingStart}_{$billingEnd}";
$cached = Cache::get($cacheKey);
if ($cached) {
return $cached;
}
// Check if usage record already exists in database
$usage = Database::selectOne(
"SELECT * FROM " . static::$table . "
WHERE company_id = ? AND billing_start = ? AND billing_end = ?
LIMIT 1",
[$companyId, $billingStart, $billingEnd]
);
if (!$usage) {
// Initialize new record
try {
$id = self::create([
'company_id' => $companyId,
'billing_start' => $billingStart,
'billing_end' => $billingEnd,
'request_count' => 0,
'voice_count' => 0,
'ocr_count' => 0
]);
$usage = [
'id' => $id,
'company_id' => $companyId,
'billing_start' => $billingStart,
'billing_end' => $billingEnd,
'request_count' => 0,
'voice_count' => 0,
'ocr_count' => 0
];
} catch (\Exception $e) {
// Handle concurrent insertions gracefully
$usage = Database::selectOne(
"SELECT * FROM " . static::$table . "
WHERE company_id = ? AND billing_start = ? AND billing_end = ?
LIMIT 1",
[$companyId, $billingStart, $billingEnd]
);
if (!$usage) {
throw $e;
}
}
}
// Cache usage data for 60 seconds (short TTL since usage changes frequently)
Cache::set($cacheKey, $usage, 60);
return $usage;
}
/**
* Increment usage counts for the current billing cycle
*/
public static function incrementUsage(int $companyId, string $type = 'request', int $amount = 1): bool
{
$activeSub = CompanySubscription::findActiveByCompany($companyId);
if (!$activeSub) {
return false;
}
$currentUsage = self::getOrCreateCurrentUsage($companyId, $activeSub);
$column = 'request_count';
if ($type === 'voice') {
$column = 'voice_count';
} elseif ($type === 'ocr') {
$column = 'ocr_count';
}
$success = Database::execute(
"UPDATE " . static::$table . "
SET {$column} = {$column} + ?
WHERE id = ?",
[$amount, $currentUsage['id']]
) > 0;
if ($success) {
// Sync Redis cache with incremented values
$cacheKey = "company_usage_{$companyId}_{$currentUsage['billing_start']}_{$currentUsage['billing_end']}";
$cached = Cache::get($cacheKey);
if ($cached) {
$cached[$column] += $amount;
Cache::set($cacheKey, $cached, 60);
}
}
return $success;
}
/**
* Check if a company has exceeded its plan limits for a certain action
*/
public static function hasRemainingLimit(int $companyId, string $type = 'request'): bool
{
$activeSub = CompanySubscription::findActiveByCompany($companyId);
if (!$activeSub) {
return false; // No active subscription means no requests allowed
}
$usage = self::getOrCreateCurrentUsage($companyId, $activeSub);
if ($type === 'request') {
return $usage['request_count'] < $activeSub['max_requests'];
} elseif ($type === 'voice') {
return $usage['voice_count'] < $activeSub['max_voice_requests'];
} elseif ($type === 'ocr') {
return $usage['ocr_count'] < $activeSub['max_ocr_requests'];
}
return false;
}
}