2023-11-17 14:44:01 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
|
2025-07-14 00:33:04 +08:00
|
|
|
|
use App\Models\User;
|
2025-07-19 14:22:01 +08:00
|
|
|
|
use App\Models\TrafficResetLog;
|
2025-06-22 01:18:38 +08:00
|
|
|
|
use App\Services\TrafficResetService;
|
2023-11-17 14:44:01 +08:00
|
|
|
|
use Illuminate\Console\Command;
|
2025-06-22 01:18:38 +08:00
|
|
|
|
use Illuminate\Support\Facades\Log;
|
2023-11-17 14:44:01 +08:00
|
|
|
|
|
|
|
|
|
|
class ResetTraffic extends Command
|
|
|
|
|
|
{
|
2025-07-19 14:22:01 +08:00
|
|
|
|
protected $signature = 'reset:traffic {--fix-null : 修正模式,重新计算next_reset_at为null的用户}';
|
|
|
|
|
|
|
|
|
|
|
|
protected $description = '流量重置 - 处理所有需要重置的用户';
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
|
|
private readonly TrafficResetService $trafficResetService
|
|
|
|
|
|
) {
|
2025-06-22 17:38:17 +08:00
|
|
|
|
parent::__construct();
|
2025-06-22 01:18:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function handle(): int
|
|
|
|
|
|
{
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$fixNull = $this->option('fix-null');
|
2025-06-22 01:18:38 +08:00
|
|
|
|
|
|
|
|
|
|
$this->info('🚀 开始执行流量重置任务...');
|
|
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
if ($fixNull) {
|
|
|
|
|
|
$this->warn('🔧 修正模式 - 将重新计算next_reset_at为null的用户');
|
2023-11-17 14:44:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 01:18:38 +08:00
|
|
|
|
try {
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$result = $fixNull ? $this->performFix() : $this->performReset();
|
|
|
|
|
|
$this->displayResults($result, $fixNull);
|
2025-06-22 01:18:38 +08:00
|
|
|
|
return self::SUCCESS;
|
2025-04-14 02:12:42 +08:00
|
|
|
|
|
2025-06-22 01:18:38 +08:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
$this->error("❌ 任务执行失败: {$e->getMessage()}");
|
2025-04-14 02:12:42 +08:00
|
|
|
|
|
2025-06-22 01:18:38 +08:00
|
|
|
|
Log::error('流量重置命令执行失败', [
|
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
|
'trace' => $e->getTraceAsString(),
|
|
|
|
|
|
]);
|
2025-05-07 19:48:19 +08:00
|
|
|
|
|
2025-06-22 01:18:38 +08:00
|
|
|
|
return self::FAILURE;
|
2025-05-07 19:48:19 +08:00
|
|
|
|
}
|
2025-06-22 01:18:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
private function displayResults(array $result, bool $fixNull): void
|
2025-06-22 01:18:38 +08:00
|
|
|
|
{
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$this->info("✅ 任务完成!\n");
|
|
|
|
|
|
|
|
|
|
|
|
if ($fixNull) {
|
|
|
|
|
|
$this->displayFixResults($result);
|
2025-06-22 01:18:38 +08:00
|
|
|
|
} else {
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$this->displayExecutionResults($result);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-22 01:18:38 +08:00
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
private function displayFixResults(array $result): void
|
|
|
|
|
|
{
|
|
|
|
|
|
$this->info("📊 修正结果统计:");
|
|
|
|
|
|
$this->info("🔍 发现用户总数: {$result['total_found']}");
|
|
|
|
|
|
$this->info("✅ 成功修正数量: {$result['total_fixed']}");
|
|
|
|
|
|
$this->info("⏱️ 总执行时间: {$result['duration']} 秒");
|
|
|
|
|
|
|
|
|
|
|
|
if ($result['error_count'] > 0) {
|
|
|
|
|
|
$this->warn("⚠️ 错误数量: {$result['error_count']}");
|
|
|
|
|
|
$this->warn("详细错误信息请查看日志");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$this->info("✨ 无错误发生");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($result['total_found'] > 0) {
|
|
|
|
|
|
$avgTime = round($result['duration'] / $result['total_found'], 4);
|
|
|
|
|
|
$this->info("⚡ 平均处理速度: {$avgTime} 秒/用户");
|
2025-06-22 01:18:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function displayExecutionResults(array $result): void
|
2025-06-22 01:18:38 +08:00
|
|
|
|
{
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$this->info("📊 执行结果统计:");
|
|
|
|
|
|
$this->info("👥 处理用户总数: {$result['total_processed']}");
|
|
|
|
|
|
$this->info("🔄 重置用户数量: {$result['total_reset']}");
|
|
|
|
|
|
$this->info("⏱️ 总执行时间: {$result['duration']} 秒");
|
|
|
|
|
|
|
|
|
|
|
|
if ($result['error_count'] > 0) {
|
|
|
|
|
|
$this->warn("⚠️ 错误数量: {$result['error_count']}");
|
|
|
|
|
|
$this->warn("详细错误信息请查看日志");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$this->info("✨ 无错误发生");
|
|
|
|
|
|
}
|
2025-06-22 01:18:38 +08:00
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
if ($result['total_processed'] > 0) {
|
|
|
|
|
|
$avgTime = round($result['duration'] / $result['total_processed'], 4);
|
|
|
|
|
|
$this->info("⚡ 平均处理速度: {$avgTime} 秒/用户");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function performReset(): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
$totalResetCount = 0;
|
|
|
|
|
|
$errors = [];
|
2025-06-22 01:18:38 +08:00
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$users = $this->getResetQuery()->get();
|
|
|
|
|
|
|
|
|
|
|
|
if ($users->isEmpty()) {
|
2025-06-22 01:18:38 +08:00
|
|
|
|
$this->info("😴 当前没有需要重置的用户");
|
|
|
|
|
|
return [
|
2025-07-19 14:22:01 +08:00
|
|
|
|
'total_processed' => 0,
|
|
|
|
|
|
'total_reset' => 0,
|
|
|
|
|
|
'error_count' => 0,
|
|
|
|
|
|
'duration' => round(microtime(true) - $startTime, 2),
|
2025-06-22 01:18:38 +08:00
|
|
|
|
];
|
2025-05-07 19:48:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
$this->info("找到 {$users->count()} 个需要重置的用户");
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($users as $user) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
$totalResetCount += (int) $this->trafficResetService->checkAndReset($user, TrafficResetLog::SOURCE_CRON);
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
$errors[] = [
|
|
|
|
|
|
'user_id' => $user->id,
|
|
|
|
|
|
'email' => $user->email,
|
|
|
|
|
|
'error' => $e->getMessage(),
|
2025-06-22 01:18:38 +08:00
|
|
|
|
];
|
2025-07-19 14:22:01 +08:00
|
|
|
|
Log::error('用户流量重置失败', [
|
|
|
|
|
|
'user_id' => $user->id,
|
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
|
]);
|
2025-06-22 01:18:38 +08:00
|
|
|
|
}
|
2025-07-19 14:22:01 +08:00
|
|
|
|
}
|
2025-06-22 01:18:38 +08:00
|
|
|
|
|
2025-07-19 14:22:01 +08:00
|
|
|
|
return [
|
|
|
|
|
|
'total_processed' => $users->count(),
|
|
|
|
|
|
'total_reset' => $totalResetCount,
|
|
|
|
|
|
'error_count' => count($errors),
|
|
|
|
|
|
'duration' => round(microtime(true) - $startTime, 2),
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function performFix(): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
$nullUsers = $this->getNullResetTimeUsers();
|
|
|
|
|
|
|
|
|
|
|
|
if ($nullUsers->isEmpty()) {
|
|
|
|
|
|
$this->info("✅ 没有发现next_reset_at为null的用户");
|
|
|
|
|
|
return [
|
|
|
|
|
|
'total_found' => 0,
|
|
|
|
|
|
'total_fixed' => 0,
|
|
|
|
|
|
'error_count' => 0,
|
|
|
|
|
|
'duration' => round(microtime(true) - $startTime, 2),
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$this->info("🔧 发现 {$nullUsers->count()} 个next_reset_at为null的用户,开始修正...");
|
|
|
|
|
|
|
|
|
|
|
|
$fixedCount = 0;
|
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($nullUsers as $user) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
$nextResetTime = $this->trafficResetService->calculateNextResetTime($user);
|
|
|
|
|
|
if ($nextResetTime) {
|
|
|
|
|
|
$user->next_reset_at = $nextResetTime->timestamp;
|
|
|
|
|
|
$user->save();
|
|
|
|
|
|
$fixedCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
$errors[] = [
|
|
|
|
|
|
'user_id' => $user->id,
|
|
|
|
|
|
'email' => $user->email,
|
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
|
];
|
|
|
|
|
|
Log::error('修正用户next_reset_at失败', [
|
|
|
|
|
|
'user_id' => $user->id,
|
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
|
]);
|
2025-06-22 01:18:38 +08:00
|
|
|
|
}
|
2023-11-17 14:44:01 +08:00
|
|
|
|
}
|
2025-06-22 01:18:38 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
2025-07-19 14:22:01 +08:00
|
|
|
|
'total_found' => $nullUsers->count(),
|
|
|
|
|
|
'total_fixed' => $fixedCount,
|
|
|
|
|
|
'error_count' => count($errors),
|
|
|
|
|
|
'duration' => round(microtime(true) - $startTime, 2),
|
2025-06-22 01:18:38 +08:00
|
|
|
|
];
|
|
|
|
|
|
}
|
2025-07-19 14:22:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function getResetQuery()
|
|
|
|
|
|
{
|
|
|
|
|
|
return User::where('next_reset_at', '<=', time())
|
|
|
|
|
|
->whereNotNull('next_reset_at')
|
|
|
|
|
|
->where(function ($query) {
|
|
|
|
|
|
$query->where('expired_at', '>', time())
|
|
|
|
|
|
->orWhereNull('expired_at');
|
|
|
|
|
|
})
|
|
|
|
|
|
->where('banned', 0)
|
|
|
|
|
|
->whereNotNull('plan_id');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function getNullResetTimeUsers()
|
|
|
|
|
|
{
|
|
|
|
|
|
return User::whereNull('next_reset_at')
|
|
|
|
|
|
->whereNotNull('plan_id')
|
|
|
|
|
|
->where(function ($query) {
|
|
|
|
|
|
$query->where('expired_at', '>', time())
|
|
|
|
|
|
->orWhereNull('expired_at');
|
|
|
|
|
|
})
|
|
|
|
|
|
->where('banned', 0)
|
|
|
|
|
|
->with('plan:id,name,reset_traffic_method')
|
|
|
|
|
|
->get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-22 01:18:38 +08:00
|
|
|
|
}
|