info('开始检查和重置第三方服务配额...'); try { $quotas = $this->getQuotasToReset(); if ($quotas->isEmpty()) { $this->info('没有找到需要重置的配额'); return Command::SUCCESS; } $this->displayQuotasToReset($quotas); if ($this->option('dry-run')) { $this->info('这是预览模式,没有实际重置配额'); return Command::SUCCESS; } if (!$this->option('force') && !$this->confirm('确认要重置这些配额吗?')) { $this->info('操作已取消'); return Command::SUCCESS; } $resetCount = $this->resetQuotas($quotas); $this->info("成功重置了 {$resetCount} 个配额"); return Command::SUCCESS; } catch (\Exception $e) { $this->error("配额重置失败: {$e->getMessage()}"); return Command::FAILURE; } } /** * 获取需要重置的配额 * * @return \Illuminate\Database\Eloquent\Collection */ protected function getQuotasToReset() { $query = ThirdPartyQuota::with('service')->where('is_active', true); // 按服务过滤 if ($serviceId = $this->option('service')) { $query->where('service_id', $serviceId); } // 按类型过滤 if ($type = $this->option('type')) { if (!QUOTA_TYPE::tryFrom($type)) { throw new \Exception("无效的配额类型: {$type}"); } $query->where('type', $type); } // 如果强制重置,返回所有匹配的配额 if ($this->option('force')) { return $query->get(); } // 否则只返回需要重置的配额 return $query->get()->filter(function ($quota) { return $quota->needsReset(); }); } /** * 显示将要重置的配额 * * @param \Illuminate\Database\Eloquent\Collection $quotas * @return void */ protected function displayQuotasToReset($quotas): void { $this->info("\n将要重置的配额:"); $this->line(str_repeat('-', 100)); $headers = ['服务', '配额类型', '限制值', '已使用', '使用率', '状态', '重置原因']; $rows = []; foreach ($quotas as $quota) { $usagePercentage = round($quota->getUsagePercentage(), 2) . '%'; $status = $quota->getStatusLabel(); $reason = ''; if ($this->option('force')) { $reason = '强制重置'; } elseif ($quota->needsReset()) { if ($quota->window_end && now()->gt($quota->window_end)) { $reason = '时间窗口已过期'; } elseif ($quota->reset_at && now()->gte($quota->reset_at)) { $reason = '到达重置时间'; } else { $reason = '需要重置'; } } $rows[] = [ $quota->service->name, $quota->getTypeLabel(), number_format($quota->limit_value), number_format($quota->used_value), $usagePercentage, $status, $reason, ]; } $this->table($headers, $rows); } /** * 重置配额 * * @param \Illuminate\Database\Eloquent\Collection $quotas * @return int */ protected function resetQuotas($quotas): int { $resetCount = 0; $progressBar = $this->output->createProgressBar($quotas->count()); $progressBar->start(); foreach ($quotas as $quota) { try { $quota->resetQuota(); $resetCount++; $this->line("\n重置配额: {$quota->service->name} - {$quota->getTypeLabel()}"); } catch (\Exception $e) { $this->line("\n重置失败: {$quota->service->name} - {$quota->getTypeLabel()}: {$e->getMessage()}"); } $progressBar->advance(); } $progressBar->finish(); $this->line(''); return $resetCount; } /** * 显示配额统计信息 * * @return void */ protected function displayQuotaStats(): void { $totalQuotas = ThirdPartyQuota::where('is_active', true)->count(); $exceededQuotas = ThirdPartyQuota::where('is_active', true) ->where('is_exceeded', true) ->count(); $nearThresholdQuotas = ThirdPartyQuota::where('is_active', true) ->whereRaw('(used_value / limit_value * 100) >= alert_threshold') ->count(); $this->info("\n配额统计:"); $this->line("总配额数: {$totalQuotas}"); $this->line("已超限配额: {$exceededQuotas}"); $this->line("接近阈值配额: {$nearThresholdQuotas}"); if ($exceededQuotas > 0) { $this->warn("\n警告: 有 {$exceededQuotas} 个配额已超限,可能影响服务调用"); } } /** * 验证配额类型 * * @param string $type * @return bool */ protected function validateQuotaType(string $type): bool { return QUOTA_TYPE::tryFrom($type) !== null; } /** * 获取所有可用的配额类型 * * @return array */ protected function getAvailableQuotaTypes(): array { return array_map(fn($case) => $case->value, QUOTA_TYPE::cases()); } }