AutoStatisticsUserHourlyTraffic.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Models\Node;
  4. use App\Models\User;
  5. use App\Models\UserDataFlowLog;
  6. use App\Models\UserHourlyDataFlow;
  7. use Illuminate\Console\Command;
  8. use Log;
  9. class AutoStatisticsUserHourlyTraffic extends Command
  10. {
  11. protected $signature = 'autoStatisticsUserHourlyTraffic';
  12. protected $description = '自动统计用户每小时流量';
  13. public function handle(): void
  14. {
  15. $logPrefix = '[用户流量统计]';
  16. $jobStartTime = microtime(true);
  17. echo "🟢 开始执行 autoStatisticsUserHourlyTraffic...\n";
  18. Log::info("$logPrefix 🟢 开始执行 autoStatisticsUserHourlyTraffic");
  19. $startTime = strtotime('-1 hour');
  20. $endTime = time();
  21. $records = UserDataFlowLog::whereBetween('log_time', [$startTime, $endTime])
  22. ->selectRaw('user_id, node_id, SUM(u) as sum_u, SUM(d) as sum_d')
  23. ->groupBy('user_id', 'node_id')
  24. ->get();
  25. $userTotals = [];
  26. $userWeightedTotals = [];
  27. $checkedUsers = [];
  28. $checkedNodes = [];
  29. $maxUserId = null;
  30. $maxTraffic = 0;
  31. foreach ($records as $record) {
  32. $user_id = $record->user_id;
  33. $node_id = $record->node_id;
  34. // 判断用户是否存在(带缓存)
  35. if (!isset($checkedUsers[$user_id])) {
  36. $checkedUsers[$user_id] = User::where('id', $user_id)->exists();
  37. }
  38. if (!$checkedUsers[$user_id]) {
  39. Log::warning("$logPrefix 跳过记录,用户不存在: user_id=$user_id");
  40. continue;
  41. }
  42. // 判断节点是否存在(带缓存)
  43. if (!isset($checkedNodes[$node_id])) {
  44. $node = Node::find($node_id);
  45. $checkedNodes[$node_id] = $node ?: false;
  46. }
  47. if (!$checkedNodes[$node_id]) {
  48. Log::warning("$logPrefix 跳过记录,节点不存在: node_id=$node_id");
  49. continue;
  50. }
  51. $u = $record->sum_u ?? 0;
  52. $d = $record->sum_d ?? 0;
  53. $total = $u + $d;
  54. $rate = floatval($checkedNodes[$node_id]->traffic_rate ?? 1);
  55. $weighted = $rate > 0 ? $total / $rate : $total;
  56. UserHourlyDataFlow::create([
  57. 'user_id' => $user_id,
  58. 'node_id' => $node_id,
  59. 'u' => $u,
  60. 'd' => $d,
  61. 'total' => $total,
  62. 'traffic' => flowAutoShow($total),
  63. ]);
  64. $userTotals[$user_id] = ($userTotals[$user_id] ?? 0) + $total;
  65. $userWeightedTotals[$user_id] = ($userWeightedTotals[$user_id] ?? 0) + $weighted;
  66. if ($userWeightedTotals[$user_id] > $maxTraffic) {
  67. $maxTraffic = $userWeightedTotals[$user_id];
  68. $maxUserId = $user_id;
  69. }
  70. }
  71. $trafficBanValue = sysConfig('traffic_ban_value');
  72. foreach ($userWeightedTotals as $user_id => $total) {
  73. if ($total > $trafficBanValue * GB) {
  74. $user = User::find($user_id);
  75. $email = $user->email ?? '未知邮箱';
  76. Log::warning("$logPrefix ⚠️ 用户 [ID: $user_id, 邮箱: $email] 最近1小时流量异常:已使用 " . flowAutoShow($total));
  77. echo "⚠️ 异常用户: $email, 使用 " . flowAutoShow($total) . "\n";
  78. }
  79. }
  80. if ($maxUserId !== null) {
  81. $maxUser = User::find($maxUserId);
  82. $email = $maxUser->email ?? '未知邮箱';
  83. $rawTraffic = 0;
  84. $rawUpload = 0;
  85. $rawDownload = 0;
  86. $convertedTraffic = $userWeightedTotals[$maxUserId] ?? 0;
  87. $details = [];
  88. foreach ($records as $record) {
  89. if ($record->user_id == $maxUserId) {
  90. $u = $record->sum_u ?? 0;
  91. $d = $record->sum_d ?? 0;
  92. $total = $u + $d;
  93. $node = $checkedNodes[$record->node_id];
  94. $rate = floatval($node->traffic_rate ?? 1);
  95. $nodeName = $node->name ?? "未知节点";
  96. if ($rate == 1) {
  97. $rawUpload += $u;
  98. $rawDownload += $d;
  99. $rawTraffic += $total;
  100. } else {
  101. $converted = $rate > 0 ? $total / $rate : $total;
  102. $details[] = "节点[$nodeName] 倍率 $rate => 上传: " . flowAutoShow($u) .
  103. ", 下载: " . flowAutoShow($d) .
  104. ", 总计: " . flowAutoShow($total) .
  105. " => 换算后(按倍率 {$rate}): " . flowAutoShow($converted);
  106. }
  107. }
  108. }
  109. $detailsText = count($details) > 0 ? " 🔁 明细: " . implode(",", $details) : "未使用大于1倍的线路";
  110. $message = "🚨 过去1小时使用流量最多的用户:$email (ID: $maxUserId),🟦 上行: " . flowAutoShow($rawUpload) .
  111. ",🟥 下行: " . flowAutoShow($rawDownload) .
  112. ",原始流量: " . flowAutoShow($rawTraffic) .
  113. "(仅倍率为1的线路),换算为1倍率流量: " . flowAutoShow($convertedTraffic) .
  114. $detailsText;
  115. echo "$message\n";
  116. Log::info("$logPrefix $message");
  117. }
  118. $jobUsedTime = round((microtime(true) - $jobStartTime), 4);
  119. Log::info("$logPrefix ✅ autoStatisticsUserHourlyTraffic 执行完成,耗时 {$jobUsedTime} 秒");
  120. echo "✅ 执行完成,耗时 {$jobUsedTime} 秒\n";
  121. $nextRunTimestamp = time() + 3600;
  122. $nextRunTimeFormatted = date('Y-m-d H:i:s', $nextRunTimestamp);
  123. echo "⏰ 下次预计运行时间:$nextRunTimeFormatted\n";
  124. Log::info("$logPrefix ⏰ 下次预计运行时间:$nextRunTimeFormatted");
  125. }
  126. private function statisticsByNode($user_id, $node_id = null): void
  127. {
  128. $query = UserDataFlowLog::whereUserId($user_id)
  129. ->whereBetween('log_time', [strtotime('-1 hour'), time()]);
  130. if ($node_id) {
  131. $query->whereNodeId($node_id);
  132. }
  133. $sums = $query->selectRaw('SUM(u) as sum_u, SUM(d) as sum_d')->first();
  134. $u = $sums->sum_u ?? 0;
  135. $d = $sums->sum_d ?? 0;
  136. $total = $u + $d;
  137. if ($total > 0) {
  138. Log::info("📊 保存流量记录 - user: $user_id, node: " . ($node_id ?? '总计') . ", u: $u, d: $d, total: $total");
  139. UserHourlyDataFlow::create([
  140. 'user_id' => $user_id,
  141. 'node_id' => $node_id,
  142. 'u' => $u,
  143. 'd' => $d,
  144. 'total' => $total,
  145. 'traffic' => flowAutoShow($total),
  146. ]);
  147. }
  148. // 检查是否超过阈值(仅统计总流量)
  149. if (is_null($node_id)) {
  150. $trafficBanValue = sysConfig('traffic_ban_value');
  151. if ($total > $trafficBanValue * GB) {
  152. $user = User::find($user_id);
  153. $email = $user->email ?? '未知邮箱';
  154. Log::warning("⚠️ 用户 [ID: $user_id, 邮箱: $email] 最近1小时流量异常:已使用 ".flowAutoShow($total));
  155. echo "⚠️ 异常用户: $email, 使用 ".flowAutoShow($total)."\n";
  156. }
  157. }
  158. }
  159. }