feat: [subscription] usage() API에 AI 토큰 사용량 통합
- api_calls 섹션 제거, ai_tokens 섹션으로 교체 - 월별 토큰 집계: 총 요청수, 입출력 토큰, 비용(USD/KRW) - 모델별 사용량 내역 (by_model) - 한도/사용율/경고 기준(80%) 포함 - tenant_id 기반 구독 조회로 변경 (subscription_id 미연결 대응)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ApiRequestLog;
|
||||
use App\Models\Tenants\AiTokenUsage;
|
||||
use App\Models\Tenants\DataExport;
|
||||
use App\Models\Tenants\Payment;
|
||||
use App\Models\Tenants\Plan;
|
||||
@@ -304,13 +304,14 @@ public function resume(int $id): Subscription
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 사용량 조회
|
||||
* 사용량 조회 (이용현황 통합)
|
||||
* - 사용자 수, 저장공간, AI 토큰, 구독 정보
|
||||
*/
|
||||
public function usage(): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$tenant = Tenant::with(['subscription.plan'])->findOrFail($tenantId);
|
||||
$tenant = Tenant::withoutGlobalScopes()->findOrFail($tenantId);
|
||||
|
||||
// 사용자 수
|
||||
$userCount = $tenant->users()->count();
|
||||
@@ -320,22 +321,42 @@ public function usage(): array
|
||||
$storageUsed = $tenant->storage_used ?? 0;
|
||||
$storageLimit = $tenant->storage_limit ?? 0;
|
||||
|
||||
// API 호출 수 (일간 - 로그는 1일 보관)
|
||||
$apiCallsUsed = ApiRequestLog::where('tenant_id', $tenantId)
|
||||
->whereDate('created_at', now()->toDateString())
|
||||
->count();
|
||||
// 기본 일간 API 호출 제한 (10,000회)
|
||||
$apiCallsLimit = 10000;
|
||||
// AI 토큰 (이번 달)
|
||||
$currentMonth = now()->format('Y-m');
|
||||
$aiTokenLimit = $tenant->ai_token_limit ?? 1000000;
|
||||
|
||||
// 구독 정보
|
||||
$subscription = $tenant->subscription;
|
||||
$remainingDays = null;
|
||||
$planName = null;
|
||||
$aiStats = AiTokenUsage::where('tenant_id', $tenantId)
|
||||
->whereRaw("DATE_FORMAT(created_at, '%Y-%m') = ?", [$currentMonth])
|
||||
->selectRaw('
|
||||
COUNT(*) as total_requests,
|
||||
COALESCE(SUM(prompt_tokens), 0) as prompt_tokens,
|
||||
COALESCE(SUM(completion_tokens), 0) as completion_tokens,
|
||||
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
||||
COALESCE(SUM(cost_usd), 0) as cost_usd,
|
||||
COALESCE(SUM(cost_krw), 0) as cost_krw
|
||||
')
|
||||
->first();
|
||||
|
||||
if ($subscription && $subscription->is_valid) {
|
||||
$remainingDays = $subscription->remaining_days;
|
||||
$planName = $subscription->plan?->name;
|
||||
}
|
||||
$aiByModel = AiTokenUsage::where('tenant_id', $tenantId)
|
||||
->whereRaw("DATE_FORMAT(created_at, '%Y-%m') = ?", [$currentMonth])
|
||||
->selectRaw('
|
||||
model,
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
||||
COALESCE(SUM(cost_krw), 0) as cost_krw
|
||||
')
|
||||
->groupBy('model')
|
||||
->orderByDesc('total_tokens')
|
||||
->get();
|
||||
|
||||
$totalTokens = (int) $aiStats->total_tokens;
|
||||
$aiPercentage = $aiTokenLimit > 0 ? round(($totalTokens / $aiTokenLimit) * 100, 1) : 0;
|
||||
|
||||
// 구독 정보 (tenant_id 기반 최신 활성 구독)
|
||||
$subscription = Subscription::with('plan')
|
||||
->where('tenant_id', $tenantId)
|
||||
->orderByDesc('created_at')
|
||||
->first();
|
||||
|
||||
return [
|
||||
'users' => [
|
||||
@@ -350,17 +371,34 @@ public function usage(): array
|
||||
'limit_formatted' => $tenant->getStorageLimitFormatted(),
|
||||
'percentage' => $storageLimit > 0 ? round(($storageUsed / $storageLimit) * 100, 1) : 0,
|
||||
],
|
||||
'api_calls' => [
|
||||
'used' => $apiCallsUsed,
|
||||
'limit' => $apiCallsLimit,
|
||||
'percentage' => $apiCallsLimit > 0 ? round(($apiCallsUsed / $apiCallsLimit) * 100, 1) : 0,
|
||||
'ai_tokens' => [
|
||||
'period' => $currentMonth,
|
||||
'total_requests' => (int) $aiStats->total_requests,
|
||||
'total_tokens' => $totalTokens,
|
||||
'prompt_tokens' => (int) $aiStats->prompt_tokens,
|
||||
'completion_tokens' => (int) $aiStats->completion_tokens,
|
||||
'limit' => $aiTokenLimit,
|
||||
'percentage' => $aiPercentage,
|
||||
'cost_usd' => round((float) $aiStats->cost_usd, 4),
|
||||
'cost_krw' => round((float) $aiStats->cost_krw),
|
||||
'warning_threshold' => 80,
|
||||
'is_over_limit' => $totalTokens > $aiTokenLimit,
|
||||
'by_model' => $aiByModel->map(fn ($m) => [
|
||||
'model' => $m->model,
|
||||
'requests' => (int) $m->requests,
|
||||
'total_tokens' => (int) $m->total_tokens,
|
||||
'cost_krw' => round((float) $m->cost_krw),
|
||||
])->values()->toArray(),
|
||||
],
|
||||
'subscription' => [
|
||||
'plan' => $planName,
|
||||
'status' => $subscription?->status,
|
||||
'remaining_days' => $remainingDays,
|
||||
'plan' => $subscription?->plan?->name,
|
||||
'monthly_fee' => (int) ($subscription?->plan?->price ?? 0),
|
||||
'status' => $subscription?->status ?? 'active',
|
||||
'started_at' => $subscription?->started_at?->toDateString(),
|
||||
'ended_at' => $subscription?->ended_at?->toDateString(),
|
||||
'remaining_days' => $subscription?->ended_at
|
||||
? max(0, (int) now()->diffInDays($subscription->ended_at, false))
|
||||
: null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user