TaskDaily.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Models\Node;
  4. use App\Models\Ticket;
  5. use App\Models\User;
  6. use App\Notifications\TicketClosed;
  7. use App\Services\OrderService;
  8. use App\Utils\Helpers;
  9. use Illuminate\Console\Command;
  10. use Illuminate\Database\Eloquent\Builder;
  11. use Log;
  12. class TaskDaily extends Command
  13. {
  14. protected $signature = 'task:daily';
  15. protected $description = '每日任务';
  16. public function handle(): void
  17. {
  18. $jobTime = microtime(true);
  19. $this->expireUser(); // 过期用户处理
  20. $this->closeTickets(); // 关闭用户超时未处理的工单
  21. if (sysConfig('reset_traffic')) {
  22. $this->resetUserTraffic(); // 重置用户流量
  23. }
  24. if (sysConfig('auto_release_port')) {
  25. $this->releaseAccountPort(); // 账号端口回收
  26. }
  27. $this->userTrafficStatistics(); // 用户每日流量统计
  28. $this->nodeTrafficStatistics(); // 节点每日流量统计
  29. $jobTime = round(microtime(true) - $jobTime, 4);
  30. Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
  31. }
  32. private function expireUser(): void
  33. { // 过期用户处理
  34. $isBanStatus = sysConfig('is_ban_status');
  35. User::activeUser()
  36. ->where('expired_at', '<', date('Y-m-d')) // 过期
  37. ->chunk(config('tasks.chunk'), function ($users) use ($isBanStatus) {
  38. $dirtyWorks = [
  39. 'u' => 0,
  40. 'd' => 0,
  41. 'transfer_enable' => 0,
  42. 'enable' => 0,
  43. 'level' => 0,
  44. 'reset_time' => null,
  45. 'ban_time' => null,
  46. ]; // 停止服务
  47. $banMsg = __('[Daily Task] Account Expiration: Stop Service');
  48. if ($isBanStatus) {
  49. $dirtyWorks['status'] = -1; // 封禁账号
  50. $banMsg = __('[Daily Task] Account Expiration: Block Login & Clear Account');
  51. }
  52. foreach ($users as $user) {
  53. $user->update($dirtyWorks);
  54. Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, 0, $banMsg);
  55. $user->banedLogs()->create(['description' => $banMsg]);
  56. if ($isBanStatus) {
  57. $user->invites()->whereStatus(0)->update(['status' => 2]); // 废除其名下邀请码
  58. }
  59. }
  60. });
  61. }
  62. private function closeTickets(): void
  63. { // 关闭用户超时未处理的工单
  64. Ticket::whereStatus(1)
  65. ->whereHas('reply', function ($q) {
  66. $q->where('admin_id', '<>', null);
  67. })
  68. ->has('reply')
  69. ->where('updated_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.close.tickets').' hours')))
  70. ->chunk(config('tasks.chunk'), function ($tickets) {
  71. foreach ($tickets as $ticket) {
  72. if ($ticket->close()) {
  73. $ticket->user->notify(new TicketClosed($ticket->id, $ticket->title,
  74. route('replyTicket', ['id' => $ticket->id]),
  75. __('You have not responded this ticket in :num hours, System has closed your ticket.', ['num' => config('tasks.close.tickets')])));
  76. }
  77. }
  78. });
  79. }
  80. private function resetUserTraffic(): void
  81. { // 重置用户流量
  82. User::where('status', '<>', -1)
  83. ->where('expired_at', '>', date('Y-m-d'))
  84. ->where('reset_time', '<=', date('Y-m-d'))
  85. ->with('orders')->whereHas('orders')
  86. ->chunk(config('tasks.chunk'), function ($users) {
  87. foreach ($users as $user) {
  88. $order = $user->orders()->activePlan()->first(); // 取出用户正在使用的套餐
  89. if (! $order) {// 无套餐用户跳过
  90. Log::error('用户[ID:'.$user->id.'] 流量重置, 用户订单获取失败');
  91. continue;
  92. }
  93. $user->orders()->activePackage()->update(['is_expire' => 1]); // 过期生效中的加油包
  94. $oldData = $user->transfer_enable;
  95. // 重置流量与重置日期
  96. if ($user->update((new OrderService($order))->resetTimeAndData($user->expired_at))) {
  97. Helpers::addUserTrafficModifyLog($order->user_id, $oldData, $user->transfer_enable, __('[Daily Task] Reset Account Traffic, Next Reset Date: :date', ['date' => $user->reset_date]), $order->id);
  98. } else {
  99. Log::error("[每日任务]用户ID: $user->id | 邮箱: $user->username 流量重置失败");
  100. }
  101. }
  102. });
  103. }
  104. private function releaseAccountPort(): void
  105. { // 被封禁 / 过期N天 的账号自动释放端口
  106. User::where('port', '<>', 0)
  107. ->where(function ($query) {
  108. $query->whereStatus(-1)->orWhere('expired_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.release_port').' days')));
  109. })
  110. ->update(['port' => 0]);
  111. }
  112. private function userTrafficStatistics(): void
  113. {
  114. $created_at = date('Y-m-d 23:59:59', strtotime('-1 days'));
  115. $end = strtotime($created_at);
  116. $start = $end - 86399;
  117. // todo: laravel10 得改
  118. User::activeUser()->with('dataFlowLogs')->whereHas('dataFlowLogs', function (Builder $query) use ($start, $end) {
  119. $query->whereBetween('log_time', [$start, $end]);
  120. })->chunk(config('tasks.chunk'), function ($users) use ($start, $end, $created_at) {
  121. foreach ($users as $user) {
  122. $logs = $user->dataFlowLogs()
  123. ->whereBetween('log_time', [$start, $end])
  124. ->groupBy('node_id')
  125. ->selectRaw('node_id, sum(`u`) as u, sum(`d`) as d')
  126. ->get();
  127. $data = $logs->each(function ($log) use ($created_at) {
  128. $log->total = $log->u + $log->d;
  129. $log->traffic = formatBytes($log->total);
  130. $log->created_at = $created_at;
  131. })->flatten()->toArray();
  132. $data[] = [ // 每日节点流量合计
  133. 'u' => $logs->sum('u'),
  134. 'd' => $logs->sum('d'),
  135. 'total' => $logs->sum('total'),
  136. 'traffic' => formatBytes($logs->sum('total')),
  137. 'created_at' => $created_at,
  138. ];
  139. $user->dailyDataFlows()->createMany($data);
  140. }
  141. });
  142. }
  143. private function nodeTrafficStatistics(): void
  144. {
  145. $created_at = date('Y-m-d 23:59:59', strtotime('-1 day'));
  146. $end = strtotime($created_at);
  147. $start = $end - 86399;
  148. Node::orderBy('id')->with('userDataFlowLogs')->whereHas('userDataFlowLogs', function (Builder $query) use ($start, $end) {
  149. $query->whereBetween('log_time', [$start, $end]);
  150. })->chunk(config('tasks.chunk'), function ($nodes) use ($start, $end, $created_at) {
  151. foreach ($nodes as $node) {
  152. $traffic = $node->userDataFlowLogs()
  153. ->whereBetween('log_time', [$start, $end])
  154. ->selectRaw('sum(`u`) as u, sum(`d`) as d')->first();
  155. $total = $traffic->u + $traffic->d;
  156. $node->dailyDataFlows()->create([
  157. 'u' => $traffic->u,
  158. 'd' => $traffic->d,
  159. 'total' => $total,
  160. 'traffic' => formatBytes($total),
  161. 'created_at' => $created_at,
  162. ]);
  163. }
  164. });
  165. }
  166. }