Browse Source

Tasks optimization & Fixed Bugs

- Tasks optimization
- Fixed manual order modification may cause bugs
- Improve User modification update VNET
- Minor tweaks
BrettonYe 2 years ago
parent
commit
524c4e0d52
67 changed files with 1086 additions and 1058 deletions
  1. 1 0
      .gitignore
  2. 2 2
      app/Components/Helpers.php
  3. 2 4
      app/Console/Commands/AutoClearLogs.php
  4. 0 126
      app/Console/Commands/DailyJob.php
  5. 7 4
      app/Console/Commands/DailyNodeReport.php
  6. 0 45
      app/Console/Commands/NodeDailyTrafficStatistics.php
  7. 0 45
      app/Console/Commands/NodeHourlyTrafficStatistics.php
  8. 1 2
      app/Console/Commands/NodeStatusDetection.php
  9. 4 24
      app/Console/Commands/ServiceTimer.php
  10. 55 76
      app/Console/Commands/TaskAuto.php
  11. 186 0
      app/Console/Commands/TaskDaily.php
  12. 94 0
      app/Console/Commands/TaskHourly.php
  13. 33 0
      app/Console/Commands/TaskMonthly.php
  14. 0 26
      app/Console/Commands/UpdateUserSpeedLimit.php
  15. 0 55
      app/Console/Commands/UserDailyTrafficStatistics.php
  16. 1 2
      app/Console/Commands/UserExpireWarning.php
  17. 0 70
      app/Console/Commands/UserHourlyTrafficMonitoring.php
  18. 1 2
      app/Console/Commands/UserTrafficWarning.php
  19. 1 2
      app/Console/Commands/VNetReload.php
  20. 15 21
      app/Console/Kernel.php
  21. 5 1
      app/Http/Controllers/Admin/LogsController.php
  22. 2 2
      app/Http/Controllers/Admin/SubscribeController.php
  23. 3 6
      app/Http/Controllers/Admin/UserController.php
  24. 1 3
      app/Http/Controllers/Api/Client/ClientController.php
  25. 27 16
      app/Http/Controllers/UserController.php
  26. 1 1
      app/Jobs/VNet/addUser.php
  27. 5 0
      app/Models/Coupon.php
  28. 1 1
      app/Models/Node.php
  29. 32 14
      app/Models/Order.php
  30. 1 1
      app/Models/User.php
  31. 37 26
      app/Observers/OrderObserver.php
  32. 30 16
      app/Observers/UserObserver.php
  33. 40 66
      app/Services/OrderService.php
  34. 32 0
      database/migrations/2023_04_22_005731_change_subscribe_desc.php
  35. 46 0
      public/assets/global/js/Plugin/ionrangeslider.js
  36. 1 45
      public/robots.txt
  37. 66 52
      resources/lang/en.json
  38. 12 12
      resources/lang/en/admin.php
  39. 4 4
      resources/lang/en/common.php
  40. 3 3
      resources/lang/en/model.php
  41. 1 1
      resources/lang/en/notification.php
  42. 1 2
      resources/lang/en/user.php
  43. 66 52
      resources/lang/ja.json
  44. 10 10
      resources/lang/ja/admin.php
  45. 1 1
      resources/lang/ja/common.php
  46. 3 3
      resources/lang/ja/model.php
  47. 0 1
      resources/lang/ja/user.php
  48. 66 52
      resources/lang/ko.json
  49. 11 11
      resources/lang/ko/admin.php
  50. 2 2
      resources/lang/ko/common.php
  51. 3 3
      resources/lang/ko/model.php
  52. 0 1
      resources/lang/ko/user.php
  53. 66 52
      resources/lang/vi.json
  54. 11 11
      resources/lang/vi/admin.php
  55. 2 2
      resources/lang/vi/common.php
  56. 3 3
      resources/lang/vi/model.php
  57. 0 1
      resources/lang/vi/user.php
  58. 66 52
      resources/lang/zh_CN.json
  59. 11 11
      resources/lang/zh_CN/admin.php
  60. 1 1
      resources/lang/zh_CN/common.php
  61. 3 3
      resources/lang/zh_CN/model.php
  62. 0 1
      resources/lang/zh_CN/user.php
  63. 3 3
      resources/views/admin/logs/order.blade.php
  64. 1 1
      resources/views/admin/subscribe/index.blade.php
  65. 1 1
      resources/views/admin/subscribe/log.blade.php
  66. 1 1
      resources/views/user/index.blade.php
  67. 1 1
      resources/views/user/knowledge.blade.php

+ 1 - 0
.gitignore

@@ -26,3 +26,4 @@ _ide_helper.php
 node_modules/*
 composer.phar
 public/uploads/*
+composer.lock

+ 2 - 2
app/Components/Helpers.php

@@ -187,13 +187,13 @@ class Helpers
      * 记录流量变动日志.
      *
      * @param  int  $userId  用户ID
-     * @param  int|null  $orderId  订单ID
      * @param  int  $before  记录前的值
      * @param  int  $after  记录后的值
      * @param  string  $description  描述
+     * @param  int|null  $orderId  订单ID
      * @return bool
      */
-    public static function addUserTrafficModifyLog($userId, $orderId, $before, $after, $description = ''): bool
+    public static function addUserTrafficModifyLog(int $userId, int $before, int $after, string $description = '', $orderId = null): bool
     {
         $log = new UserDataModifyLog();
         $log->user_id = $userId;

+ 2 - 4
app/Console/Commands/AutoClearLogs.php

@@ -29,14 +29,12 @@ class AutoClearLogs extends Command
     {
         $jobTime = microtime(true);
 
-        // 清除日志
         if (sysConfig('is_clear_log')) {
-            $this->clearLog();
+            $this->clearLog(); // 清除日志
         }
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
     // 清除日志

+ 0 - 126
app/Console/Commands/DailyJob.php

@@ -1,126 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Components\Helpers;
-use App\Models\Ticket;
-use App\Models\User;
-use App\Notifications\TicketClosed;
-use App\Services\OrderService;
-use Illuminate\Console\Command;
-use Log;
-
-class DailyJob extends Command
-{
-    protected $signature = 'dailyJob';
-    protected $description = '每日任务';
-
-    public function handle()
-    {
-        $jobTime = microtime(true);
-
-        $this->expireUser(); // 过期用户处理
-        $this->closeTickets(); // 关闭用户超时未处理的工单
-
-        if (sysConfig('reset_traffic')) {// 重置用户流量
-            $this->resetUserTraffic();
-        }
-
-        $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
-    }
-
-    private function expireUser()// 过期用户处理
-    {
-        $isBanStatus = sysConfig('is_ban_status');
-        User::activeUser()
-            ->where('expired_at', '<', date('Y-m-d'))
-            ->chunk(config('tasks.chunk'), function ($users) use ($isBanStatus) {
-                foreach ($users as $user) {
-                    if ($isBanStatus) {
-                        $user->update([ // 停止服务 & 封禁账号
-                            'u'               => 0,
-                            'd'               => 0,
-                            'transfer_enable' => 0,
-                            'enable'          => 0,
-                            'level'           => 0,
-                            'reset_time'      => null,
-                            'ban_time'        => null,
-                            'status'          => -1,
-                        ]);
-                        $user->banedLogs()->create(['description' => '【禁止登录,清空账户】-账号已过期']);
-
-                        // 废除其名下邀请码
-                        $user->invites()->whereStatus(0)->update(['status' => 2]);
-
-                        // 写入用户流量变动记录
-                        Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, 0, '[定时任务]账号已过期(禁止登录,清空账户)');
-                    } else {
-                        $user->update([ // 停止服务
-                            'u'               => 0,
-                            'd'               => 0,
-                            'transfer_enable' => 0,
-                            'enable'          => 0,
-                            'level'           => 0,
-                            'reset_time'      => null,
-                            'ban_time'        => null,
-                        ]);
-                        $user->banedLogs()->create(['description' => '【封禁代理,清空账户】-账号已过期']);
-
-                        // 写入用户流量变动记录
-                        Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, 0, '[定时任务]账号已过期(封禁代理,清空账户)');
-                    }
-                }
-            });
-    }
-
-    private function closeTickets()// 关闭用户超时未处理的工单
-    {
-        Ticket::whereStatus(1)
-            ->whereHas('reply', function ($q) {
-                $q->where('admin_id', '<>', null);
-            })
-            ->has('reply')
-            ->where('updated_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.close.tickets').' hours')))
-            ->chunk(config('tasks.chunk'), function ($tickets) {
-                foreach ($tickets as $ticket) {
-                    if ($ticket->close()) {
-                        $ticket->user->notify(new TicketClosed($ticket->id, $ticket->title, route('replyTicket', ['id' => $ticket->id]),
-                            __('You have not responded this ticket in :num hours, System has closed your ticket.', ['num' => config('tasks.close.tickets')])));
-                    }
-                }
-            });
-    }
-
-    private function resetUserTraffic()// 重置用户流量
-    {
-        User::where('status', '<>', -1)
-            ->where('expired_at', '>', date('Y-m-d'))
-            ->whereNotNull('reset_time')
-            ->where('reset_time', '<=', date('Y-m-d'))
-            ->with('orders')->whereHas('orders')
-            ->chunk(config('tasks.chunk'), function ($users) {
-                foreach ($users as $user) {
-                    $order = $user->orders()->activePlan()->first(); // 取出用户正在使用的套餐
-
-                    if (! $order) {// 无套餐用户跳过
-                        continue;
-                    }
-
-                    $user->orders()->activePackage()->update(['is_expire' => 1]); // 过期生效中的加油包
-
-                    $oldData = $user->transfer_enable;
-                    // 重置流量与重置日期
-                    if ($user->update((new OrderService($order))->resetTimeAndData($user->expired_at))) {
-                        // 可用流量变动日志
-                        Helpers::addUserTrafficModifyLog($order->user_id, $order->id, $oldData, $user->transfer_enable, '【流量重置任务】重置可用流量');
-                        Log::notice('用户[ID:'.$user->id.'  昵称: '.$user->nickname.'  邮箱: '.$user->username.'] 流量重置为 '.flowAutoShow($user->transfer_enable).'. 重置日期为 '.
-                        ($user->reset_date ?: '【无】'));
-                    } else {
-                        Log::alert('用户[ID:'.$user->id.'  昵称: '.$user->nickname.'  邮箱: '.$user->username.'] 流量重置失败');
-                    }
-                }
-            });
-    }
-}

+ 7 - 4
app/Console/Commands/DailyNodeReport.php

@@ -6,6 +6,7 @@ use App\Models\Node;
 use App\Models\User;
 use App\Notifications\NodeDailyReport;
 use Illuminate\Console\Command;
+use Illuminate\Database\Eloquent\Builder;
 use Log;
 use Notification;
 
@@ -19,13 +20,16 @@ class DailyNodeReport extends Command
         $jobTime = microtime(true);
 
         if (sysConfig('node_daily_notification')) {
-            $nodeList = Node::whereStatus(1)->with('dailyDataFlows')->get();
+            $date = date('Y-m-d', strtotime('-1 days'));
+            $nodeList = Node::with('dailyDataFlows')->whereHas('dailyDataFlows', function (Builder $query) use ($date) {
+                $query->whereDate('created_at', $date);
+            })->get();
             if ($nodeList->isNotEmpty()) {
                 $data = [];
                 $upload = 0;
                 $download = 0;
                 foreach ($nodeList as $node) {
-                    $log = $node->dailyDataFlows()->whereDate('created_at', date('Y-m-d', strtotime('-1 days')))->first();
+                    $log = $node->dailyDataFlows()->whereDate('created_at', $date)->first();
                     $data[] = [
                         'name'     => $node->name,
                         'upload'   => flowAutoShow($log->u ?? 0),
@@ -49,7 +53,6 @@ class DailyNodeReport extends Command
         }
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 }

+ 0 - 45
app/Console/Commands/NodeDailyTrafficStatistics.php

@@ -1,45 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Models\Node;
-use Illuminate\Console\Command;
-use Log;
-
-class NodeDailyTrafficStatistics extends Command
-{
-    protected $signature = 'nodeDailyTrafficStatistics';
-    protected $description = '节点每日流量统计';
-
-    public function handle()
-    {
-        $jobTime = microtime(true);
-
-        foreach (Node::whereRelayNodeId(null)->whereStatus(1)->orderBy('id')->with('userDataFlowLogs')->whereHas('userDataFlowLogs')->get() as $node) {
-            $this->statisticsByNode($node);
-        }
-
-        $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
-    }
-
-    private function statisticsByNode(Node $node)
-    {
-        $created_at = date('Y-m-d 23:59:59', strtotime('-1 days'));
-        $time = strtotime($created_at);
-        $traffic = $node->userDataFlowLogs()
-            ->whereBetween('log_time', [$time - 86399, $time])
-            ->selectRaw('sum(`u`) as u, sum(`d`) as d')->first();
-
-        if ($traffic && $total = $traffic->u + $traffic->d) { // 有数据才记录
-            $node->dailyDataFlows()->create([
-                'u'          => $traffic->u,
-                'd'          => $traffic->d,
-                'total'      => $total,
-                'traffic'    => flowAutoShow($total),
-                'created_at' => $created_at,
-            ]);
-        }
-    }
-}

+ 0 - 45
app/Console/Commands/NodeHourlyTrafficStatistics.php

@@ -1,45 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Models\Node;
-use Illuminate\Console\Command;
-use Log;
-
-class NodeHourlyTrafficStatistics extends Command
-{
-    protected $signature = 'nodeHourlyTrafficStatistics';
-    protected $description = '节点每小时流量统计';
-
-    public function handle()
-    {
-        $jobTime = microtime(true);
-
-        foreach (Node::whereRelayNodeId(null)->whereStatus(1)->orderBy('id')->with('userDataFlowLogs')->whereHas('userDataFlowLogs')->get() as $node) {
-            $this->statisticsByNode($node);
-        }
-
-        $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
-    }
-
-    private function statisticsByNode(Node $node)
-    {
-        $created_at = date('Y-m-d H:59:59', strtotime('-1 hour'));
-        $time = strtotime($created_at);
-        $traffic = $node->userDataFlowLogs()
-            ->whereBetween('log_time', [$time - 3599, $time])
-            ->selectRaw('sum(`u`) as u, sum(`d`) as d')->first();
-
-        if ($traffic && $total = $traffic->u + $traffic->d) { // 有数据才记录
-            $node->hourlyDataFlows()->create([
-                'u'          => $traffic->u,
-                'd'          => $traffic->d,
-                'total'      => $total,
-                'traffic'    => flowAutoShow($total),
-                'created_at' => $created_at,
-            ]);
-        }
-    }
-}

+ 1 - 2
app/Console/Commands/NodeStatusDetection.php

@@ -35,8 +35,7 @@ class NodeStatusDetection extends Command
         }
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info("---【{$this->description}】完成---,耗时 {$jobTime} 秒");
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
     private function checkNodeStatus()

+ 4 - 24
app/Console/Commands/ServiceTimer.php

@@ -2,7 +2,6 @@
 
 namespace App\Console\Commands;
 
-use App\Components\Helpers;
 use App\Models\Order;
 use Illuminate\Console\Command;
 use Log;
@@ -16,37 +15,18 @@ class ServiceTimer extends Command
     {
         $jobTime = microtime(true);
 
-        $this->decGoodsTraffic(); // 扣减用户到期商品的流量
+        $this->expiredPlan(); // 过期套餐
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    // 扣减用户到期商品的流量
-    private function decGoodsTraffic()
+    private function expiredPlan()
     {
-        //获取失效的套餐
         Order::activePlan()
             ->where('expired_at', '<=', date('Y-m-d H:i:s'))
-            ->with('user')->whereHas('user') // 无用户订单,跳过
             ->chunk(config('tasks.chunk'), function ($orders) {
-                foreach ($orders as $order) {
-                    $user = $order->user;
-
-                    $user->update([ // 清理全部流量,重置重置日期和等级
-                        'u'               => 0,
-                        'd'               => 0,
-                        'transfer_enable' => 0,
-                        'reset_time'      => null,
-                        'level'           => 0,
-                    ]);
-                    Helpers::addUserTrafficModifyLog($user->id, $order->id, $user->transfer_enable, 0, '[定时任务]用户所购商品到期,扣减商品对应的流量');
-
-                    sleep(1); // 保证一切都已经更新到位
-
-                    $order->update(['is_expire' => 1]); // 过期本订单
-                }
+                $orders->each->expired(); // 过期订单
             });
     }
 }

+ 55 - 76
app/Console/Commands/AutoJob.php → app/Console/Commands/TaskAuto.php

@@ -2,21 +2,21 @@
 
 namespace App\Console\Commands;
 
-use App\Components\Helpers;
 use App\Models\Config;
 use App\Models\Coupon;
 use App\Models\Invite;
 use App\Models\Order;
 use App\Models\User;
+use App\Models\UserSubscribe;
 use App\Models\VerifyCode;
 use Illuminate\Console\Command;
 use Illuminate\Database\Eloquent\Builder;
 use Log;
 
-class AutoJob extends Command
+class TaskAuto extends Command
 {
-    protected $signature = 'autoJob';
-    protected $description = '自动任务';
+    protected $signature = 'task:auto';
+    protected $description = '自动任务';
 
     /*
      * 警告:除非熟悉业务流程,否则不推荐更改以下执行顺序,随意变更以下顺序可能导致系统异常
@@ -25,38 +25,36 @@ class AutoJob extends Command
     {
         $jobTime = microtime(true);
 
-        Order::recentUnPay()->chunk(config('tasks.chunk'), function ($orders) {
-            $orders->each->close();
-        }); // 关闭超时未支付本地订单
-
-        Order::whereStatus(1)->where('created_at', '<=', date('Y-m-d H:i:s', strtotime('-'.config('tasks.close.confirmation_orders').' hours')))->chunk(config('tasks.chunk'), function ($orders) {
-            $orders->each->close();
-        }); // 关闭未处理的人工支付订单
-
-        $this->expireCode(); //过期验证码、优惠券、邀请码无效化
-
+        $this->orderTimer(); // 超时订单
+        $this->expireCode(); // 过期验证码、优惠券、邀请码无效化
         if (sysConfig('is_subscribe_ban')) {
-            $this->blockSubscribe(); // 封禁访问异常的订阅链接
+            $this->blockSubscribes(); // 封禁访问异常的订阅
         }
-
+        $this->unblockSubscribes(); // 解禁订阅
         $this->blockUsers(); // 封禁账号
         $this->unblockUsers(); // 解封被封禁的账号
 
-        if (sysConfig('auto_release_port')) {
-            $this->dispatchPort(); // 端口回收与再分配
-        }
-
-        if (sysConfig('maintenance_mode') && sysConfig('maintenance_time') && sysConfig('maintenance_time') <= date('c')) {// 检查维护模式
-            Config::whereIn('name', ['maintenance_mode', 'maintenance_content', 'maintenance_time'])->update(['value' => null]);
+        if (sysConfig('maintenance_mode') && sysConfig('maintenance_time') && sysConfig('maintenance_time') <= date('c')) { // 检查维护模式
+            Config::whereIn('name', ['maintenance_mode', 'maintenance_time'])->update(['value' => null]);
         }
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function expireCode()// 注册验证码自动置无效 & 优惠券无效化
+    private function orderTimer()
     {
+        Order::recentUnPay()->chunk(config('tasks.chunk'), function ($orders) {
+            $orders->each->close();
+        }); // 关闭超时未支付本地订单
+
+        Order::whereStatus(1)->where('created_at', '<=', date('Y-m-d H:i:s', strtotime('-'.config('tasks.close.confirmation_orders').' hours')))->chunk(config('tasks.chunk'), function ($orders) {
+            $orders->each->close();
+        }); // 关闭未处理的人工支付订单
+    }
+
+    private function expireCode()
+    { // 注册验证码自动置无效 & 优惠券无效化
         // 注册验证码自动置无效
         VerifyCode::recentUnused()->update(['status' => 2]);
 
@@ -74,42 +72,46 @@ class AutoJob extends Command
             ->update(['status' => 2]);
     }
 
-    private function blockSubscribe()// 封禁访问异常的订阅链接
-    {
-        $subscribe_ban_times = sysConfig('subscribe_ban_times');
+    private function blockSubscribes()
+    { // 封禁访问异常的订阅链接
         User::activeUser()
             ->with(['subscribe', 'subscribeLogs'])
             ->whereHas('subscribe', function (Builder $query) {
-                $query->whereStatus(0); // 获取有订阅且未被封禁用户
+                $query->whereStatus(1); // 获取有订阅且未被封禁用户
             })
-            ->chunk(config('tasks.chunk'), function ($users) use ($subscribe_ban_times) {
+            ->whereHas('subscribeLogs', function (Builder $query) {
+                $query->where('request_time', '>=', date('Y-m-d H:i:s', strtotime('-1 day'))); //    ->distinct()->count('request_ip');
+            }, '>=', sysConfig('subscribe_ban_times'))
+            ->chunk(config('tasks.chunk'), function ($users) {
+                $trafficBanTime = sysConfig('traffic_ban_time');
+                $ban_time = strtotime($trafficBanTime.' minutes');
+                $dirtyWorks = ['status' => 0, 'ban_time' => $ban_time, 'ban_desc' => 'Subscription link receive abnormal access and banned by the system'];
+                $banMsg = ['time' => $trafficBanTime, 'description' => __('[Auto Job] Blocked Subscription: Subscription with abnormal requests within 24 hours')];
                 foreach ($users as $user) {
-                    // 24小时内不同IP的请求次数
-                    $request_times = $user->subscribeLogs()
-                        ->where('request_time', '>=', date('Y-m-d H:i:s', strtotime('-1 days')))
-                        ->distinct()
-                        ->count('request_ip');
-                    if ($request_times >= $subscribe_ban_times) {
-                        $user->subscribe->update([
-                            'status'   => 0,
-                            'ban_time' => strtotime(sysConfig('traffic_ban_time').' minutes'),
-                            'ban_desc' => '存在异常,自动封禁',
-                        ]);
-                        $user->banedLogs()->create(['description' => '【完全封禁订阅】-订阅24小时内请求异常']); // 记录封禁日志
-                    }
+                    $user->subscribe->update($dirtyWorks);
+                    $user->banedLogs()->create($banMsg); // 记录封禁日志
                 }
             });
     }
 
-    private function blockUsers()// 封禁账号
+    private function unblockSubscribes()
     {
+        UserSubscribe::whereStatus(0)->where('ban_time', '<=', time())->chunk(config('tasks.chunk'), function ($subscribes) {
+            foreach ($subscribes as $subscribe) {
+                $subscribe->update(['status' => 1, 'ban_time' => null, 'ban_desc' => null]);
+            }
+        });
+    }
+
+    private function blockUsers()
+    { // 封禁账号
         // 禁用流量超限用户
         User::activeUser()
             ->whereRaw('u + d >= transfer_enable')
             ->chunk(config('tasks.chunk'), function ($users) {
                 foreach ($users as $user) {
                     $user->update(['enable' => 0]);
-                    $user->banedLogs()->create(['description' => '【封禁代理】-流量已用完']); // 写入日志
+                    $user->banedLogs()->create(['description' => __('[Auto Job] Blocked service: Run out of traffic')]); // 写入日志
                 }
             });
 
@@ -119,34 +121,31 @@ class AutoJob extends Command
             User::activeUser()
                 ->whereBanTime(null)
                 ->chunk(config('tasks.chunk'), function ($users) use ($trafficBanTime) {
+                    $ban_time = strtotime($trafficBanTime.' minutes');
                     foreach ($users as $user) {
                         // 多往前取5分钟,防止数据统计任务执行时间过长导致没有数据
                         if ($user->isTrafficWarning()) {
-                            $user->update([
-                                'enable'   => 0,
-                                'ban_time' => strtotime($trafficBanTime.' minutes'),
-                            ]);
-                            $user->banedLogs()->create(['time' => $trafficBanTime, 'description' => '【临时封禁代理】-1小时内流量异常']); // 写入日志
+                            $user->update(['enable' => 0, 'ban_time' => $ban_time]);
+                            $user->banedLogs()->create(['time' => $trafficBanTime, 'description' => __('[Auto Job] Blocked service: Abnormal traffic within 1 hour')]); // 写入日志
                         }
                     }
                 });
         }
     }
 
-    private function unblockUsers()// 解封被临时封禁的账号
-    {
+    private function unblockUsers()
+    { // 解封账号
         // 解封被临时封禁的账号
         User::bannedUser()
-            ->whereNotNull('ban_time')
             ->where('ban_time', '<', time())
             ->chunk(config('tasks.chunk'), function ($users) {
                 foreach ($users as $user) {
                     $user->update(['enable' => 1, 'ban_time' => null]);
-                    $user->banedLogs()->create(['description' => '【自动解封】-临时封禁到期']); // 写入操作日志
+                    $user->banedLogs()->create(['description' => '[自动任务]解封服务: 封禁到期']); // 写入操作日志
                 }
             });
 
-        // 可用流量大于已用流量也解封(比如:邀请返利自动加了流量)
+        // 可用流量大于已用流量也解封(比如:邀请返利加了流量)
         User::bannedUser()
             ->whereBanTime(null)
             ->where('expired_at', '>=', date('Y-m-d'))
@@ -154,28 +153,8 @@ class AutoJob extends Command
             ->chunk(config('tasks.chunk'), function ($users) {
                 foreach ($users as $user) {
                     $user->update(['enable' => 1]);
-                    $user->banedLogs()->create(['description' => '【自动解封】-有流量解封']); // 写入操作日志
+                    $user->banedLogs()->create(['description' => '[自动任务]解封服务: 出现可用流量']); // 写入操作日志
                 }
             });
     }
-
-    // 端口回收与分配
-    private function dispatchPort()
-    {
-        // 自动分配端口
-        User::activeUser()
-            ->wherePort(0)
-            ->chunk(config('tasks.chunk'), function ($users) {
-                foreach ($users as $user) {
-                    $user->update(['port' => Helpers::getPort()]);
-                }
-            });
-
-        // 被封禁 / 过期xx天 的账号自动释放端口
-        User::where('port', '<>', 0)
-            ->where(function ($query) {
-                $query->whereStatus(-1)->orWhere('expired_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.release_port').' days')));
-            })
-            ->update(['port' => 0]);
-    }
 }

+ 186 - 0
app/Console/Commands/TaskDaily.php

@@ -0,0 +1,186 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Components\Helpers;
+use App\Models\Node;
+use App\Models\Ticket;
+use App\Models\User;
+use App\Notifications\TicketClosed;
+use App\Services\OrderService;
+use Illuminate\Console\Command;
+use Illuminate\Database\Eloquent\Builder;
+use Log;
+
+class TaskDaily extends Command
+{
+    protected $signature = 'task:daily';
+    protected $description = '每日任务';
+
+    public function handle()
+    {
+        $jobTime = microtime(true);
+
+        $this->expireUser(); // 过期用户处理
+        $this->closeTickets(); // 关闭用户超时未处理的工单
+        if (sysConfig('reset_traffic')) {
+            $this->resetUserTraffic(); // 重置用户流量
+        }
+        if (sysConfig('auto_release_port')) {
+            $this->releaseAccountPort();  // 账号端口回收
+        }
+        $this->userTrafficStatistics(); // 用户每日流量统计
+        $this->nodeTrafficStatistics(); // 节点每日流量统计
+
+        $jobTime = round(microtime(true) - $jobTime, 4);
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
+    }
+
+    private function expireUser()// 过期用户处理
+    {
+        $isBanStatus = sysConfig('is_ban_status');
+        User::activeUser()
+            ->where('expired_at', '<', date('Y-m-d')) // 过期
+            ->chunk(config('tasks.chunk'), function ($users) use ($isBanStatus) {
+                $dirtyWorks = [
+                    'u'               => 0,
+                    'd'               => 0,
+                    'transfer_enable' => 0,
+                    'enable'          => 0,
+                    'level'           => 0,
+                    'reset_time'      => null,
+                    'ban_time'        => null,
+                ]; // 停止服务
+                $banMsg = __('[Daily Task] Account Expiration: Stop Service');
+                if ($isBanStatus) {
+                    $dirtyWorks['status'] = -1; // 封禁账号
+                    $banMsg = __('[Daily Task] Account Expiration: Block Login & Clear Account');
+                }
+                foreach ($users as $user) {
+                    $user->update($dirtyWorks);
+                    Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, 0, $banMsg);
+                    $user->banedLogs()->create(['description' => $banMsg]);
+                    if ($isBanStatus) {
+                        $user->invites()->whereStatus(0)->update(['status' => 2]); // 废除其名下邀请码
+                    }
+                }
+            });
+    }
+
+    private function closeTickets()// 关闭用户超时未处理的工单
+    {
+        Ticket::whereStatus(1)
+            ->whereHas('reply', function ($q) {
+                $q->where('admin_id', '<>', null);
+            })
+            ->has('reply')
+            ->where('updated_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.close.tickets').' hours')))
+            ->chunk(config('tasks.chunk'), function ($tickets) {
+                foreach ($tickets as $ticket) {
+                    if ($ticket->close()) {
+                        $ticket->user->notify(new TicketClosed($ticket->id, $ticket->title,
+                            route('replyTicket', ['id' => $ticket->id]),
+                            __('You have not responded this ticket in :num hours, System has closed your ticket.', ['num' => config('tasks.close.tickets')])));
+                    }
+                }
+            });
+    }
+
+    private function resetUserTraffic()// 重置用户流量
+    {
+        User::where('status', '<>', -1)
+            ->where('expired_at', '>', date('Y-m-d'))
+            ->where('reset_time', '<=', date('Y-m-d'))
+            ->with('orders')->whereHas('orders')
+            ->chunk(config('tasks.chunk'), function ($users) {
+                foreach ($users as $user) {
+                    $order = $user->orders()->activePlan()->first(); // 取出用户正在使用的套餐
+
+                    if (! $order) {// 无套餐用户跳过
+                        Log::error('用户[ID:'.$user->id.'] 流量重置, 用户订单获取失败');
+                        continue;
+                    }
+
+                    $user->orders()->activePackage()->update(['is_expire' => 1]); // 过期生效中的加油包
+
+                    $oldData = $user->transfer_enable;
+                    // 重置流量与重置日期
+                    if ($user->update((new OrderService($order))->resetTimeAndData($user->expired_at))) {
+                        Helpers::addUserTrafficModifyLog($order->user_id, $oldData, $user->transfer_enable, __('[Daily Task] Reset Account Traffic, Next Reset Date: :date', ['date' => $user->reset_date]), $order->id);
+                    } else {
+                        Log::error("[每日任务]用户ID: $user->id | 邮箱: $user->username 流量重置失败");
+                    }
+                }
+            });
+    }
+
+    private function releaseAccountPort()
+    {
+        // 被封禁 / 过期N天 的账号自动释放端口
+        User::where('port', '<>', 0)
+            ->where(function ($query) {
+                $query->whereStatus(-1)->orWhere('expired_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.release_port').' days')));
+            })
+            ->update(['port' => 0]);
+    }
+
+    private function userTrafficStatistics()
+    {
+        $created_at = date('Y-m-d 23:59:59', strtotime('-1 days'));
+        $end = strtotime($created_at);
+        $start = $end - 86399;
+        // todo: laravel10 得改
+        User::activeUser()->with('dataFlowLogs')->whereHas('dataFlowLogs', function (Builder $query) use ($start, $end) {
+            $query->whereBetween('log_time', [$start, $end]);
+        })->chunk(config('tasks.chunk'), function ($users) use ($start, $end, $created_at) {
+            foreach ($users as $user) {
+                $logs = $user->dataFlowLogs()
+                    ->whereBetween('log_time', [$start, $end])
+                    ->groupBy('node_id')
+                    ->selectRaw('node_id, sum(`u`) as u, sum(`d`) as d')
+                    ->get();
+
+                $data = $logs->each(function ($log) use ($created_at) {
+                    $log->total = $log->u + $log->d;
+                    $log->traffic = flowAutoShow($log->total);
+                    $log->created_at = $created_at;
+                })->flatten()->toArray();
+
+                $data[] = [ // 每日节点流量合计
+                    'u'          => $logs->sum('u'),
+                    'd'          => $logs->sum('d'),
+                    'total'      => $logs->sum('total'),
+                    'traffic'    => flowAutoShow($logs->sum('total')),
+                    'created_at' => $created_at,
+                ];
+
+                $user->dailyDataFlows()->createMany($data);
+            }
+        });
+    }
+
+    private function nodeTrafficStatistics()
+    {
+        $created_at = date('Y-m-d 23:59:59', strtotime('-1 day'));
+        $end = strtotime($created_at);
+        $start = $end - 86399;
+
+        Node::orderBy('id')->with('userDataFlowLogs')->whereHas('userDataFlowLogs', function (Builder $query) use ($start, $end) {
+            $query->whereBetween('log_time', [$start, $end]);
+        })->chunk(config('tasks.chunk'), function ($nodes) use ($start, $end, $created_at) {
+            foreach ($nodes as $node) {
+                $traffic = $node->userDataFlowLogs()
+                    ->whereBetween('log_time', [$start, $end])
+                    ->selectRaw('sum(`u`) as u, sum(`d`) as d')->first();
+                $total = $traffic->u + $traffic->d;
+                $node->dailyDataFlows()->create([
+                    'u'          => $traffic->u,
+                    'd'          => $traffic->d,
+                    'total'      => $total,
+                    'traffic'    => flowAutoShow($total),
+                    'created_at' => $created_at,
+                ]);
+            }
+        });
+    }
+}

+ 94 - 0
app/Console/Commands/TaskHourly.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\Node;
+use App\Models\User;
+use App\Notifications\DataAnomaly;
+use Illuminate\Console\Command;
+use Illuminate\Database\Eloquent\Builder;
+use Log;
+use Notification;
+
+class TaskHourly extends Command
+{
+    protected $signature = 'task:hourly';
+
+    protected $description = '每小时任务';
+
+    public function handle(): void
+    {
+        $jobTime = microtime(true);
+
+        $this->userTrafficStatistics(); // 用户小时流量统计
+        $this->nodeTrafficStatistics(); // 节点小时流量统计
+
+        $jobTime = round(microtime(true) - $jobTime, 4);
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
+    }
+
+    private function userTrafficStatistics()
+    {
+        $created_at = date('Y-m-d H:59:59', strtotime('-1 hour'));
+        $end = strtotime($created_at);
+        $start = $end - 3599;
+        $data_anomaly_notification = sysConfig('data_anomaly_notification');
+        $traffic_ban_value = sysConfig('traffic_ban_value') * GB;
+        User::activeUser()->with('dataFlowLogs')->WhereHas('dataFlowLogs')->whereHas('dataFlowLogs', function (Builder $query) use ($start, $end) {
+            $query->whereBetween('log_time', [$start, $end]);
+        })->chunk(config('tasks.chunk'), function ($users) use ($traffic_ban_value, $start, $end, $created_at, $data_anomaly_notification) {
+            foreach ($users as $user) {
+                $logs = $user->dataFlowLogs()
+                    ->whereBetween('log_time', [$start, $end])
+                    ->groupBy('node_id')
+                    ->selectRaw('node_id, sum(`u`) as u, sum(`d`) as d')
+                    ->get();
+
+                $data = $logs->each(function ($log) use ($created_at) {
+                    $log->total = $log->u + $log->d;
+                    $log->traffic = flowAutoShow($log->total);
+                    $log->created_at = $created_at;
+                })->flatten()->toArray();
+                $overall = [ // 每小时节点流量合计
+                    'u'          => $logs->sum('u'),
+                    'd'          => $logs->sum('d'),
+                    'total'      => $logs->sum('total'),
+                    'traffic'    => flowAutoShow($logs->sum('total')),
+                    'created_at' => $created_at,
+                ];
+                $data[] = $overall;
+                $user->hourlyDataFlows()->createMany($data);
+
+                // 用户流量异常警告
+                if ($data_anomaly_notification && $overall['total'] >= $traffic_ban_value) {
+                    Notification::send(User::find(1), new DataAnomaly($user->username, flowAutoShow($overall['u']), flowAutoShow($overall['d']), $overall['traffic']));
+                }
+            }
+        });
+    }
+
+    private function nodeTrafficStatistics()
+    {
+        $created_at = date('Y-m-d H:59:59', strtotime('-1 hour'));
+        $end = strtotime($created_at);
+        $start = $end - 3599;
+
+        Node::orderBy('id')->with('userDataFlowLogs')->whereHas('userDataFlowLogs', function (Builder $query) use ($start, $end) {
+            $query->whereBetween('log_time', [$start, $end]);
+        })->chunk(config('tasks.chunk'), function ($nodes) use ($start, $end, $created_at) {
+            foreach ($nodes as $node) {
+                $traffic = $node->userDataFlowLogs()
+                    ->whereBetween('log_time', [$start, $end])
+                    ->selectRaw('sum(`u`) as u, sum(`d`) as d')->first();
+                $total = $traffic->u + $traffic->d;
+                $node->hourlyDataFlows()->create([
+                    'u'          => $traffic->u,
+                    'd'          => $traffic->d,
+                    'total'      => $total,
+                    'traffic'    => flowAutoShow($total),
+                    'created_at' => $created_at,
+                ]);
+            }
+        });
+    }
+}

+ 33 - 0
app/Console/Commands/TaskMonthly.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\User;
+use Illuminate\Console\Command;
+use Log;
+
+class TaskMonthly extends Command
+{
+    protected $signature = 'task:monthly';
+
+    protected $description = '每月任务';
+
+    public function handle(): void
+    {
+        $jobTime = microtime(true);
+
+        if (sysConfig('data_exhaust_notification')) {
+            $this->cleanAccounts(); // 用户流量超过警告阈值提醒
+        }
+
+        $jobTime = round(microtime(true) - $jobTime, 4);
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
+    }
+
+    private function cleanAccounts()
+    {
+        // 账号遗留结算的流量
+        User::where('expired_at', '<', date('Y-m-d'))->where('transfer_enable', '==', 0)->whereEnable(0)
+            ->whereRaw('u + d > transfer_enable')->update(['u' => 0, 'd' => 0]);
+    }
+}

+ 0 - 26
app/Console/Commands/UpdateUserSpeedLimit.php

@@ -1,26 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Models\Order;
-use Illuminate\Console\Command;
-use Log;
-
-class UpdateUserSpeedLimit extends Command
-{
-    protected $signature = 'updateUserSpeedLimit';
-    protected $description = '根据商品更新用户限速';
-
-    public function handle()
-    {
-        $jobTime = microtime(true);
-
-        foreach (Order::whereStatus(2)->whereIsExpire(0)->where('goods_id', '<>', null)->oldest()->with(['user', 'goods'])->has('goods')->has('user')->get() as $order) {
-            $order->user->update(['speed_limit' => $order->goods->speed_limit]);
-        }
-
-        $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
-    }
-}

+ 0 - 55
app/Console/Commands/UserDailyTrafficStatistics.php

@@ -1,55 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Models\User;
-use Illuminate\Console\Command;
-use Log;
-
-class UserDailyTrafficStatistics extends Command
-{
-    protected $signature = 'userDailyTrafficStatistics';
-    protected $description = '用户每日流量统计';
-
-    public function handle()
-    {
-        $jobTime = microtime(true);
-        User::activeUser()->with('dataFlowLogs')->WhereHas('dataFlowLogs')->chunk(config('tasks.chunk'), function ($users) {
-            foreach ($users as $user) {
-                $this->statisticsByUser($user);
-            }
-        });
-        $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
-    }
-
-    private function statisticsByUser(User $user)
-    {
-        $created_at = date('Y-m-d 23:59:59', strtotime('-1 days'));
-        $time = strtotime($created_at);
-        $logs = $user->dataFlowLogs()
-            ->whereBetween('log_time', [$time - 86399, $time])
-            ->groupBy('node_id')
-            ->selectRaw('node_id, sum(`u`) as u, sum(`d`) as d')
-            ->get();
-
-        if ($logs->isNotEmpty()) { // 有数据才记录
-            $data = $logs->each(function ($log) use ($created_at) {
-                $log->total = $log->u + $log->d;
-                $log->traffic = flowAutoShow($log->total);
-                $log->created_at = $created_at;
-            })->flatten()->toArray();
-
-            $data[] = [ // 每日节点流量合计
-                'u'          => $logs->sum('u'),
-                'd'          => $logs->sum('d'),
-                'total'      => $logs->sum('total'),
-                'traffic'    => flowAutoShow($logs->sum('total')),
-                'created_at' => $created_at,
-            ];
-
-            $user->dailyDataFlows()->createMany($data);
-        }
-    }
-}

+ 1 - 2
app/Console/Commands/UserExpireWarning.php

@@ -21,8 +21,7 @@ class UserExpireWarning extends Command
         }
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
     private function userExpireWarning()

+ 0 - 70
app/Console/Commands/UserHourlyTrafficMonitoring.php

@@ -1,70 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Models\User;
-use App\Notifications\DataAnomaly;
-use Illuminate\Console\Command;
-use Log;
-use Notification;
-
-class UserHourlyTrafficMonitoring extends Command
-{
-    protected $signature = 'userHourlyTrafficMonitoring';
-    protected $description = '用户每小时流量监控';
-    private $data_anomaly_notification;
-    private $traffic_ban_value;
-
-    public function handle()
-    {
-        $jobTime = microtime(true);
-        $this->data_anomaly_notification = sysConfig('data_anomaly_notification');
-        $this->traffic_ban_value = sysConfig('traffic_ban_value') * GB;
-
-        User::activeUser()->with('dataFlowLogs')->WhereHas('dataFlowLogs')->chunk(config('tasks.chunk'), function ($users) {
-            foreach ($users as $user) {
-                $this->statisticsByUser($user);
-            }
-        });
-
-        $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
-    }
-
-    private function statisticsByUser(User $user)
-    {
-        $created_at = date('Y-m-d H:59:59', strtotime('-1 hour'));
-        $time = strtotime($created_at);
-        $logs = $user->dataFlowLogs()
-            ->whereBetween('log_time', [$time - 3599, $time])
-            ->groupBy('node_id')
-            ->selectRaw('node_id, sum(`u`) as u, sum(`d`) as d')
-            ->get();
-
-        if ($logs->isNotEmpty()) { // 有数据才记录
-            $data = $logs->each(function ($log) use ($created_at) {
-                $log->total = $log->u + $log->d;
-                $log->traffic = flowAutoShow($log->total);
-                $log->created_at = $created_at;
-            })->flatten()->toArray();
-
-            $data[] = [ // 每小时节点流量合计
-                'u'          => $logs->sum('u'),
-                'd'          => $logs->sum('d'),
-                'total'      => $logs->sum('total'),
-                'traffic'    => flowAutoShow($logs->sum('total')),
-                'created_at' => $created_at,
-            ];
-
-            $user->hourlyDataFlows()->createMany($data);
-
-            if ($this->data_anomaly_notification) { // 用户流量异常警告
-                $traffic = $user->hourlyDataFlows()->whereNodeId(null)->latest()->first();
-                if ($traffic->total >= $this->traffic_ban_value) {
-                    Notification::send(User::find(1), new DataAnomaly($user->username, flowAutoShow($traffic->u), flowAutoShow($traffic->d), flowAutoShow($traffic->traffic)));
-                }
-            }
-        }
-    }
-}

+ 1 - 2
app/Console/Commands/UserTrafficWarning.php

@@ -21,8 +21,7 @@ class UserTrafficWarning extends Command
         }
 
         $jobTime = round(microtime(true) - $jobTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
     private function userTrafficWarning()// 用户流量超过警告阈值提醒

+ 1 - 2
app/Console/Commands/VNetReload.php

@@ -22,7 +22,6 @@ class VNetReload extends Command
         }
 
         $jobTime = round(microtime(true) - $startTime, 4);
-
-        Log::info('---【'.$this->description.'】完成---,耗时'.$jobTime.'秒');
+        Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 }

+ 15 - 21
app/Console/Kernel.php

@@ -3,16 +3,14 @@
 namespace App\Console;
 
 use App\Console\Commands\AutoClearLogs;
-use App\Console\Commands\AutoJob;
-use App\Console\Commands\DailyJob;
 use App\Console\Commands\DailyNodeReport;
-use App\Console\Commands\NodeDailyTrafficStatistics;
-use App\Console\Commands\NodeHourlyTrafficStatistics;
 use App\Console\Commands\NodeStatusDetection;
 use App\Console\Commands\ServiceTimer;
-use App\Console\Commands\UserDailyTrafficStatistics;
+use App\Console\Commands\TaskAuto;
+use App\Console\Commands\TaskDaily;
+use App\Console\Commands\TaskHourly;
+use App\Console\Commands\TaskMonthly;
 use App\Console\Commands\UserExpireWarning;
-use App\Console\Commands\UserHourlyTrafficMonitoring;
 use App\Console\Commands\UserTrafficWarning;
 use Illuminate\Console\Scheduling\Schedule;
 use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -26,16 +24,14 @@ class Kernel extends ConsoleKernel
      */
     protected $commands = [
         AutoClearLogs::class,
-        AutoJob::class,
-        DailyJob::class,
         DailyNodeReport::class,
-        NodeDailyTrafficStatistics::class,
-        NodeHourlyTrafficStatistics::class,
         NodeStatusDetection::class,
         ServiceTimer::class,
-        UserDailyTrafficStatistics::class,
+        TaskAuto::class,
+        TaskDaily::class,
+        TaskHourly::class,
+        TaskMonthly::class,
         UserExpireWarning::class,
-        UserHourlyTrafficMonitoring::class,
         UserTrafficWarning::class,
     ];
 
@@ -47,18 +43,16 @@ class Kernel extends ConsoleKernel
      */
     protected function schedule(Schedule $schedule)
     {
-        $schedule->command('serviceTimer')->everyTenMinutes();
+        $schedule->command('serviceTimer')->everyFiveMinutes();
         $schedule->command('nodeStatusDetection')->everyTenMinutes();
         $schedule->command('autoClearLogs')->everyThirtyMinutes();
-        $schedule->command('nodeHourlyTrafficStatistics')->hourly();
-        $schedule->command('userHourlyTrafficMonitoring')->hourly();
-        $schedule->command('dailyJob')->daily();
-        $schedule->command('dailyNodeReport')->dailyAt('09:00');
+        $schedule->command('task:hourly')->hourly();
+        $schedule->command('task:daily')->dailyAt('02:30');
+        $schedule->command('dailyNodeReport')->dailyAt('09:30');
         $schedule->command('userTrafficWarning')->dailyAt('10:30');
-        $schedule->command('userExpireWarning')->dailyAt('20:00');
-        $schedule->command('userDailyTrafficStatistics')->daily();
-        $schedule->command('nodeDailyTrafficStatistics')->daily();
-        $schedule->command('autoJob')->everyMinute();
+        $schedule->command('userExpireWarning')->dailyAt('20:30');
+        $schedule->command('task:auto')->everyMinute();
+        $schedule->command('task:monthly')->monthly();
     }
 
     /**

+ 5 - 1
app/Http/Controllers/Admin/LogsController.php

@@ -70,7 +70,11 @@ class LogsController extends Controller
         $order = Order::findOrFail($request->input('oid'));
         $status = (int) $request->input('status');
 
-        if ($order->update(['status' => $status])) {
+        if ($order->status === 3 && $status === 2 && $order->goods->type === 2 && Order::userActivePlan($order->user_id)->exists()) {
+            return Response::json(['status' => 'fail', 'message' => '更新失败, 订单冲突']); // 防止预支付订单假激活
+        }
+
+        if ($order->update(['is_expire' => 0, 'expired_at' => null, 'status' => $status])) {
             return Response::json(['status' => 'success', 'message' => '更新成功']);
         }
 

+ 2 - 2
app/Http/Controllers/Admin/SubscribeController.php

@@ -68,9 +68,9 @@ class SubscribeController extends Controller
     public function setSubscribeStatus(UserSubscribe $subscribe)
     {
         if ($subscribe->status) {
-            $subscribe->update(['status' => 0, 'ban_time' => time(), 'ban_desc' => '后台手动封禁']);
+            $subscribe->update(['status' => 0, 'ban_time' => strtotime(sysConfig('traffic_ban_time').' minutes'), 'ban_desc' => 'Your subscription has been disabled by the administrator, please contact the administrator to restore it']);
         } else {
-            $subscribe->update(['status' => 1, 'ban_time' => null, 'ban_desc' => '']);
+            $subscribe->update(['status' => 1, 'ban_time' => null, 'ban_desc' => null]);
         }
 
         return Response::json(['status' => 'success', 'message' => '操作成功']);

+ 3 - 6
app/Http/Controllers/Admin/UserController.php

@@ -109,8 +109,7 @@ class UserController extends Controller
             }
 
             if ($user) {
-                // 写入用户流量变动记录
-                Helpers::addUserTrafficModifyLog($user->id, null, 0, $data['transfer_enable'], '后台手动添加用户');
+                Helpers::addUserTrafficModifyLog($user->id, 0, $data['transfer_enable'], '后台手动添加用户');
 
                 return Response::json(['status' => 'success', 'message' => '添加成功']);
             }
@@ -178,8 +177,7 @@ class UserController extends Controller
         try {
             for ($i = 0; $i < (int) request('amount', 1); $i++) {
                 $user = Helpers::addUser(Str::random(8).'@auto.generate', Str::random(), 1024 * GB, 365);
-                // 写入用户流量变动记录
-                Helpers::addUserTrafficModifyLog($user->id, null, 0, 1024 * GB, trans('admin.user.massive.note'));
+                Helpers::addUserTrafficModifyLog($user->id, 0, 1024 * GB, trans('admin.user.massive.note'));
             }
 
             return Response::json(['status' => 'success', 'message' => trans('admin.user.massive.succeed')]);
@@ -247,9 +245,8 @@ class UserController extends Controller
                 $data['password'] = $password;
             }
 
-            // 写入用户流量变动记录
             if ($user->transfer_enable !== $data['transfer_enable']) {
-                Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, $data['transfer_enable'], '后台手动编辑用户');
+                Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, $data['transfer_enable'], '后台手动编辑用户');
             }
 
             if ($user->update($data)) {

+ 1 - 3
app/Http/Controllers/Api/Client/ClientController.php

@@ -172,9 +172,7 @@ class ClientController extends Controller
         if (! $user->incrementData($traffic)) {
             return response()->json(['ret' => 0, 'title' => trans('common.failed'), 'msg' => trans('user.home.attendance.failed')]);
         }
-
-        // 写入用户流量变动记录
-        Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, $user->transfer_enable + $traffic, trans('user.home.attendance.attribute'));
+        Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, $user->transfer_enable + $traffic, trans('user.home.attendance.attribute'));
 
         // 多久后可以再签到
         $ttl = sysConfig('traffic_limit_time') ? sysConfig('traffic_limit_time') * Minute : Day;

+ 27 - 16
app/Http/Controllers/UserController.php

@@ -75,6 +75,7 @@ class UserController extends Controller
             'paying_user'      => $userService->isActivePaying(), // 付费用户判断
             'userLoginLog'     => $user->loginLogs()->latest()->first(), // 近期登录日志
             'subscribe_status' => $user->subscribe->status,
+            'subMsg'           => $user->subscribe->ban_desc,
             'subType'          => $subType,
             'subUrl'           => route('sub', $user->subscribe->code),
         ], $this->dataFlowChart($user->id)));
@@ -99,9 +100,7 @@ class UserController extends Controller
         if (! $user->incrementData($traffic)) {
             return Response::json(['status' => 'fail', 'title' => trans('common.failed'), 'message' => trans('user.home.attendance.failed')]);
         }
-
-        // 写入用户流量变动记录
-        Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, $user->transfer_enable + $traffic, trans('user.home.attendance.attribute'));
+        Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, $user->transfer_enable + $traffic, trans('user.home.attendance.attribute'));
 
         // 多久后可以再签到
         $ttl = sysConfig('traffic_limit_time') ? sysConfig('traffic_limit_time') * Minute : Day;
@@ -223,7 +222,7 @@ class UserController extends Controller
         }
 
         return view('user.services', [
-            'chargeGoodsList' => Goods::type(3)->whereStatus(1)->orderBy('price')->get(),
+            'chargeGoodsList' => Goods::type(3)->orderBy('price')->get(),
             'goodsList'       => $goodsList,
             'renewTraffic'    => $renewPrice ? Helpers::getPriceTag($renewPrice) : 0,
             'dataPlusDays'    => $dataPlusDays > date('Y-m-d') ? $dataPlusDays->diffInDays() : 0,
@@ -270,16 +269,24 @@ class UserController extends Controller
 
     public function closePlan(): JsonResponse
     {
-        $activePlan = Order::userActivePlan()->firstOrFail();
-        $activePlan->is_expire = 1;
+        $activePlan = Order::userActivePlan()->first();
+        if ($activePlan) {
+            if ($activePlan->expired()) { // 关闭先前套餐后,新套餐自动运行
+                if (Order::userActivePlan()->exists()) {
+                    return Response::json(['status' => 'success', 'message' => trans('common.active_item', ['attribute' => trans('common.success')])]);
+                }
 
-        if ($activePlan->save()) {
-            // 关闭先前套餐后,新套餐自动运行
-            if (Order::userActivePlan()->exists()) {
-                return Response::json(['status' => 'success', 'message' => trans('common.active_item', ['attribute' => trans('common.success')])]);
+                return Response::json(['status' => 'success', 'message' => trans('common.close')]);
             }
+        } else {
+            $prepaidPlan = Order::userPrepay()->first();
+            if ($prepaidPlan) { // 关闭先前套餐后,新套餐自动运行
+                if ($prepaidPlan->complete()) {
+                    return Response::json(['status' => 'success', 'message' => trans('common.active_item', ['attribute' => trans('common.success')])]);
+                }
 
-            return Response::json(['status' => 'success', 'message' => trans('common.close')]);
+                return Response::json(['status' => 'success', 'message' => trans('common.close')]);
+            }
         }
 
         return Response::json(['status' => 'fail', 'message' => trans('common.close_item', ['attribute' => trans('common.failed')])]);
@@ -462,6 +469,7 @@ class UserController extends Controller
             'subType'    => $data,
             'subUrl'     => route('sub', $subscribe->code),
             'subStatus'  => $subscribe->status,
+            'subMsg'     => $subscribe->ban_desc,
             'knowledges' => Article::type(1)->lang()->orderByDesc('sort')->latest()->get()->groupBy('category'),
         ]);
     }
@@ -470,12 +478,13 @@ class UserController extends Controller
     { // 更换订阅地址
         try {
             DB::beginTransaction();
+            $user = auth()->user();
 
             // 更换订阅码
-            auth()->user()->subscribe->update(['code' => Helpers::makeSubscribeCode()]);
+            $user->subscribe->update(['code' => Helpers::makeSubscribeCode()]);
 
             // 更换连接信息
-            auth()->user()->update(['passwd' => Str::random(), 'vmess_id' => Str::uuid()]);
+            $user->update(['passwd' => Str::random(), 'vmess_id' => Str::uuid()]);
 
             DB::commit();
 
@@ -508,9 +517,11 @@ class UserController extends Controller
     public function charge(Request $request): ?JsonResponse
     {
         $validator = Validator::make($request->all(), [
-            'coupon_sn' => ['required', Rule::exists('coupon', 'sn')->where(static function ($query) {
-                $query->whereType(3)->whereStatus(0);
-            })],
+            'coupon_sn' => [
+                'required', Rule::exists('coupon', 'sn')->where(static function ($query) {
+                    $query->whereType(3)->whereStatus(0);
+                }),
+            ],
         ]);
 
         if ($validator->fails()) {

+ 1 - 1
app/Jobs/VNet/addUser.php

@@ -44,7 +44,7 @@ class addUser implements ShouldQueue
     public function handle(): void
     {
         foreach ($this->nodes as $node) {
-            if ($node->is_ddns) {
+            if (isset($node->is_ddns) && $node->is_ddns) {
                 $this->send($node->server.':'.$node->push_port, $node->auth->secret);
             } else { // 多IP支持
                 foreach ($node->ips() as $ip) {

+ 5 - 0
app/Models/Coupon.php

@@ -46,4 +46,9 @@ class Coupon extends Model
 
         return $this->save();
     }
+
+    public function isExpired()
+    {
+        return $this->attributes['end_time'] < time() || $this->attributes['status'] === 2;
+    }
 }

+ 1 - 1
app/Models/Node.php

@@ -172,7 +172,7 @@ class Node extends Model
             'protocol'     => $this->profile['protocol'] ?? '',
             'obfs'         => $this->profile['obfs'] ?? '',
             'obfs_param'   => $this->profile['obfs_param'] ?? '',
-            'is_udp'       => $this->is_udp,
+            'is_udp'       => (int) $this->is_udp,
             'speed_limit'  => $this->getRawOriginal('speed_limit'),
             'client_limit' => $this->client_limit,
             'single'       => isset($this->profile['passwd']) ? 1 : 0,

+ 32 - 14
app/Models/Order.php

@@ -57,7 +57,7 @@ class Order extends Model
 
     public function scopeUserPrepay($query, $uid = null)
     {
-        return $query->uid($uid)->whereStatus(3);
+        return $query->uid($uid)->whereStatus(3)->oldest();
     }
 
     public function scopeActive($query)
@@ -109,36 +109,54 @@ class Order extends Model
         return $this->update(['status' => 3]);
     }
 
-    // 订单状态
-    public function getStatusLabelAttribute(): string
+    public function expired() // 预支付订单
     {
-        switch ($this->attributes['status']) {
+        return $this->update(['is_expire' => 1]);
+    }
+
+    public function getStatusLabelAttribute($isHtml): string
+    { // 订单状态
+        return $this->statusTags($this->attributes['status'], $this->attributes['is_expire']);
+    }
+
+    public function statusTags($status, $expire, $isHtml = true): string
+    {
+        switch ($status) {
             case -1:
-                $status_label = '<span class="badge badge-default">'.trans('common.order.status.cancel').'</span>';
+                $label = trans('common.order.status.cancel');
                 break;
             case 0:
-                $status_label = '<span class="badge badge-danger">'.trans('common.payment.status.wait').'</span>';
+                $tag = 1;
+                $label = trans('common.payment.status.wait');
                 break;
             case 1:
-                $status_label = '<span class="badge badge-info">'.trans('common.order.status.review').'</span>';
+                $tag = 2;
+                $label = trans('common.order.status.review');
                 break;
             case 2:
                 if ($this->attributes['goods_id'] === null) {
-                    $status_label = '<span class="badge badge-default">'.trans('common.order.status.complete').'</span>';
-                } elseif ($this->attributes['is_expire']) {
-                    $status_label = '<span class="badge badge-default">'.trans('common.status.expire').'</span>';
+                    $label = trans('common.order.status.complete');
+                } elseif ($expire) {
+                    $label = trans('common.status.expire');
                 } else {
-                    $status_label = '<span class="badge badge-success">'.trans('common.order.status.ongoing').'</span>';
+                    $tag = 3;
+                    $label = trans('common.order.status.ongoing');
                 }
                 break;
             case 3:
-                $status_label = '<span class="badge badge-info">'.trans('common.order.status.prepaid').'</span>';
+                $tag = 2;
+                $label = trans('common.order.status.prepaid');
                 break;
             default:
-                $status_label = trans('common.status.unknown');
+                $tag = 4;
+                $label = trans('common.status.unknown');
+        }
+
+        if ($isHtml) {
+            $label = '<span class="badge badge-'.['default', 'danger', 'info', 'success', 'warning'][$tag ?? 0].'">'.$label.'</span>';
         }
 
-        return $status_label;
+        return $label;
     }
 
     public function getOriginAmountAttribute($value)

+ 1 - 1
app/Models/User.php

@@ -230,7 +230,7 @@ class User extends Authenticatable
 
     public function scopeBannedUser($query)
     {
-        return $query->where('status', '>=', 0)->whereEnable(0);
+        return $query->where('status', '<>', -1)->whereEnable(0);
     }
 
     public function nodes($userLevel = -1, $userGroupId = -1)

+ 37 - 26
app/Observers/OrderObserver.php

@@ -16,48 +16,59 @@ class OrderObserver
     public function updated(Order $order): void
     {
         $changes = $order->getChanges();
+        // 套餐订单-流量包订单互联
+        if (Arr::exists($changes, 'is_expire') && $changes['is_expire'] === 1 && $order->goods->type === 2) {
+            $user = $order->user;
+            $user->update([ // 清理全部流量,重置重置日期和等级
+                'u'               => 0,
+                'd'               => 0,
+                'transfer_enable' => 0,
+                'reset_time'      => null,
+                'level'           => 0,
+                'enable'          => 0,
+            ]);
+            Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, 0, __('[Service Timer] Service Expiration'), $order->id);
+
+            Order::userActivePackage($order->user_id)->update(['is_expire' => 1]); // 过期生效中的加油包
+            $this->activatePrepaidPlan($order->user_id); // 激活预支付套餐
+        }
+
         if (Arr::exists($changes, 'status')) {
             if ($changes['status'] === -1) { // 本地订单-在线订单 关闭互联
                 if ($order->payment) {
                     $order->payment->close(); // 关闭在线订单
                 }
 
-                if ($order->coupon && $this->returnCoupon($order->coupon)) { // 退回优惠券
-                    Helpers::addCouponLog('订单超时未支付,自动退回', $order->coupon_id, $order->goods_id, $order->id);
+                if ($order->coupon) { // 退回优惠券
+                    $this->returnCoupon($order, $order->coupon);
                 }
-            }
 
-            if ($changes['status'] === 1) { // 待确认支付
+                if ($order->goods && $order->goods->type === 2 && $order->getOriginal('status') === 2 && Order::userActivePlan($order->user_id)->where('id', '<>', $order->id)->doesntExist()) { // 下一个套餐
+                    $this->activatePrepaidPlan($order->user_id);
+                }
+            } elseif ($changes['status'] === 1) { // 待确认支付 通知管理
                 Notification::send(User::find(1), new PaymentConfirm($order));
-            }
-
-            // 本地订单-在线订单 支付成功互联
-            if ($changes['status'] === 2 && $order->getOriginal('status') !== 3) {
+            } elseif ($changes['status'] === 2 && $order->getOriginal('status') !== 3) { // 本地订单-在线订单 支付成功互联
                 (new OrderService($order))->receivedPayment();
+            } elseif ($changes['status'] === 3 && Order::userActivePlan($order->user_id)->doesntExist()) {
+                $this->activatePrepaidPlan($order->user_id);
             }
         }
+    }
 
-        // 套餐订单-流量包订单互联
-        if (Arr::exists($changes, 'is_expire') && $changes['is_expire'] === 1) {
-            // 过期生效中的加油包
-            Order::userActivePackage($order->user_id)->update(['is_expire' => 1]);
-
-            // 检查该订单对应用户是否有预支付套餐
-            $prepaidOrder = Order::userPrepay($order->user_id)->oldest()->first();
-
-            if ($prepaidOrder) {
-                (new OrderService($prepaidOrder))->activatePrepaidPlan(); // 激活预支付
-            }
+    private function activatePrepaidPlan(int $user_id): void
+    { // 激活[预支付订单]
+        $prepaidOrder = Order::userPrepay($user_id)->first(); // 检查该订单对应用户是否有预支付套餐
+        if ($prepaidOrder) {
+            (new OrderService($prepaidOrder))->activatePrepaidPlan(); // 激活预支付套餐
         }
     }
 
-    // 返回优惠券
-    private function returnCoupon(Coupon $coupon): bool
-    {
-        if ($coupon->type !== 3) {
-            return $coupon->update(['usable_times' => $coupon->usable_times + 1, 'status' => 0]);
+    private function returnCoupon(Order $order, Coupon $coupon): void
+    { // 退回优惠券
+        if ($coupon->type !== 3 && ! $coupon->isExpired()) {
+            Helpers::addCouponLog('订单取消, 自动退回', $order->coupon_id, $order->goods_id, $order->id);
+            $coupon->update(['usable_times' => $coupon->usable_times + 1, 'status' => 0]);
         }
-
-        return false;
     }
 }

+ 30 - 16
app/Observers/UserObserver.php

@@ -16,7 +16,7 @@ class UserObserver
         $user->subscribe()->create(['code' => Helpers::makeSubscribeCode()]);
 
         $allowNodes = $user->nodes()->whereType(4)->get();
-        if (count($allowNodes)) {
+        if ($allowNodes->isNotEmpty()) {
             addUser::dispatch($user->id, $allowNodes);
         }
     }
@@ -24,29 +24,43 @@ class UserObserver
     public function updated(User $user): void
     {
         $changes = $user->getChanges();
-        $allowNodes = $user->nodes()->whereType(4)->get();
-        $oldAllowNodes = $user->nodes($user->getOriginal('level'), $user->getOriginal('user_group_id'))->whereType(4)->get();
-        if ($allowNodes->isNotEmpty() || $oldAllowNodes->isNotEmpty()) {
-            if (Arr::hasAny($changes, ['level', 'user_group_id', 'enable'])) {
-                if (Arr::has($changes, 'enable')) {
-                    if ($user->enable) { // TODO: 由于vnet未正确使用enable字段,临时解决方案
+        $enableChange = Arr::has($changes, ['enable']);
+        if (($user->enable === 1 || $enableChange) && Arr::hasAny($changes, ['level', 'user_group_id', 'enable', 'port', 'passwd', 'speed_limit'])) {
+            $allowNodes = $user->nodes()->whereType(4)->get();
+            if (Arr::hasAny($changes, ['level', 'user_group_id'])) {
+                $oldAllowNodes = $user->nodes($user->getOriginal('level'), $user->getOriginal('user_group_id'))->whereType(4)->get();
+                if ($enableChange) {
+                    if ($user->enable === 0 && $oldAllowNodes->isNotEmpty()) {
+                        delUser::dispatch($user->id, $oldAllowNodes);
+                    } elseif ($user->enable === 1 && $allowNodes->isNotEmpty()) {
                         addUser::dispatch($user->id, $allowNodes);
-                    } else {
-                        delUser::dispatch($user->id, $allowNodes);
                     }
                 } else {
-                    // 权限修改,消除重叠的部分
-                    $old = $oldAllowNodes->diff($allowNodes);
-                    if ($oldAllowNodes->isNotEmpty() && $old->isNotEmpty()) {
-                        delUser::dispatch($user->id, $old->diff($allowNodes));
+                    $old = $oldAllowNodes->diff($allowNodes); //old 有 allow 没有
+                    $new = $allowNodes->diff($oldAllowNodes); //allow 有 old 没有
+                    if ($old->isNotEmpty()) {
+                        delUser::dispatch($user->id, $old);
                     }
-                    $new = $allowNodes->diff($oldAllowNodes);
                     if ($new->isNotEmpty()) {
                         addUser::dispatch($user->id, $new);
                     }
+                    if (Arr::hasAny($changes, ['port', 'passwd', 'speed_limit'])) {
+                        $same = $allowNodes->intersect($oldAllowNodes); // 共有部分
+                        if ($same->isNotEmpty()) {
+                            editUser::dispatch($user, $same);
+                        }
+                    }
+                }
+            } elseif ($allowNodes->isNotEmpty()) {
+                if ($enableChange) {
+                    if ($user->enable === 1) { // TODO: 由于vnet未正确使用enable字段,临时解决方案
+                        addUser::dispatch($user->id, $allowNodes);
+                    } else {
+                        delUser::dispatch($user->id, $allowNodes);
+                    }
+                } elseif (Arr::hasAny($changes, ['port', 'passwd', 'speed_limit'])) {
+                    editUser::dispatch($user, $allowNodes);
                 }
-            } elseif (Arr::hasAny($changes, ['port', 'passwd', 'speed_limit'])) {
-                editUser::dispatch($user, $allowNodes);
             }
         }
     }

+ 40 - 66
app/Services/OrderService.php

@@ -24,44 +24,38 @@ class OrderService
         self::$payment = $order->payment;
     }
 
-    // 支付成功后处理
     public function receivedPayment(): bool
-    {
-        if (self::$payment) {// 是否为余额购买套餐
-            if (self::$payment->status === 1) {// 已处理
-                return true;
-            }
+    { // 支付成功后处理
+        if (self::$payment && self::$payment->status !== 1) {// 是否为余额购买套餐
             self::$payment->complete();
-
-            // 余额充值
-            if (self::$order->goods_id === null) {
-                return $this->chargeCredit();
-            }
         }
 
-        $goods = self::$order->goods;
-        switch ($goods->type) {// 商品为流量或者套餐
-            case 1:// 流量包
-                $ret = $this->activatePackage();
-                break;
-            case 2:// 套餐
-                if (Order::userActivePlan(self::$user->id)->where('id', '<>', self::$order->id)->exists()) {// 判断套餐是否直接激活
-                    $ret = $this->setPrepaidPlan();
-                } else {
-                    $ret = $this->activatePlan();
-                }
-                $this->setCommissionExpense(self::$user); // 返利
-                break;
-            default:
-                Log::emergency('【处理订单】出现错误-未知套餐类型');
+        if (self::$order->goods_id === null) {
+            $ret = $this->chargeCredit();
+        } else {
+            $goods = self::$order->goods;
+            switch ($goods->type) {// 商品为流量或者套餐
+                case 1: // 流量包
+                    $ret = $this->activatePackage();
+                    break;
+                case 2: // 套餐
+                    if (Order::userActivePlan(self::$user->id)->where('id', '<>', self::$order->id)->exists()) {// 判断套餐是否直接激活
+                        $ret = $this->setPrepaidPlan();
+                    } else {
+                        $ret = $this->activatePlan();
+                    }
+                    $this->setCommissionExpense(self::$user); // 返利
+                    break;
+                default:
+                    Log::emergency('【处理订单】出现错误-未知套餐类型');
+            }
         }
 
         return $ret ?? true;
     }
 
-    // 余额充值
     private function chargeCredit(): bool
-    {
+    { // 余额充值
         $credit = self::$user->credit;
         $ret = self::$user->updateCredit(self::$order->origin_amount);
         // 余额变动记录日志
@@ -72,35 +66,25 @@ class OrderService
         return $ret;
     }
 
-    // 激活流量包
     private function activatePackage(): bool
-    {
+    { // 激活流量包
         if (self::$user->incrementData(self::$goods->traffic * MB)) {
-            return Helpers::addUserTrafficModifyLog(
-                self::$order->user_id,
-                self::$order->id,
-                self::$user->transfer_enable - self::$goods->traffic * MB,
-                self::$user->transfer_enable,
-                '['.self::$order->pay_way.']加上用户购买的套餐流量'
-            );
+            return Helpers::addUserTrafficModifyLog(self::$order->user_id, self::$user->transfer_enable - self::$goods->traffic * MB, self::$user->transfer_enable, '['.self::$order->pay_way.']加上用户购买的套餐流量', self::$order->id);
         }
 
         return false;
     }
 
-    // 设置预支付套餐
     private function setPrepaidPlan(): bool
-    {
-        self::$order->status = 3; // 3为预支付
-        // 预支付订单, 刷新账号有效时间用于流量重置判断
-        return self::$order->save()
-            && self::$user->update(['expired_at' => date('Y-m-d', strtotime(self::$user->expired_at.' +'.self::$goods->days.' days'))]);
+    { // 设置预支付套餐, 刷新账号有效时间用于流量重置判断
+        Order::whereId(self::$order->id)->first()->prepay(); // 直接编辑self::$order->prepay() [手动修改]会加不上
+
+        return self::$user->update(['expired_at' => date('Y-m-d', strtotime(self::$user->expired_at.' +'.self::$goods->days.' days'))]);
     }
 
-    // 激活套餐
-    private function activatePlan(): bool
-    {
-        Order::whereId(self::$order->id)->update(['expired_at' => date('Y-m-d H:i:s', strtotime(self::$goods->days.' days'))]);
+    public function activatePlan(): bool
+    { // 激活套餐
+        Order::whereId(self::$order->id)->first()->update(['expired_at' => date('Y-m-d H:i:s', strtotime(self::$goods->days.' days'))]);
         $oldData = self::$user->transfer_enable;
         $updateData = [
             'invite_num'  => self::$user->invite_num + (self::$goods->invite_num ?: 0),
@@ -115,26 +99,19 @@ class OrderService
         }
 
         if (self::$user->update(array_merge($this->resetTimeAndData(), $updateData))) {
-            return Helpers::addUserTrafficModifyLog(
-                self::$order->user_id,
-                self::$order->id,
-                $oldData,
-                self::$user->transfer_enable,
-                '【'.self::$order->pay_way.'】加上用户购买的套餐流量'
-            );
+            return Helpers::addUserTrafficModifyLog(self::$order->user_id, $oldData, self::$user->transfer_enable, '【'.self::$order->pay_way.'】加上用户购买的套餐流量', self::$order->id);
         }
 
         return false;
     }
 
-    // 计算下次重置与账号过期时间
     public function resetTimeAndData($expired_at = null): array
-    {
+    { // 计算下次重置与账号过期时间
         $data = ['u' => 0, 'd' => 0];
         // 账号有效期
         if (! $expired_at) {
             $expired_at = date('Y-m-d', strtotime(self::$goods->days.' days'));
-            foreach (Order::userPrepay(self::$order->user_id)->get() as $paidOrder) {//拿出可能存在的其余套餐, 推算最新的到期时间
+            foreach (Order::userPrepay(self::$order->user_id)->with('goods')->get() as $paidOrder) {//拿出可能存在的其余套餐, 推算最新的到期时间
                 //取出对应套餐信息
                 $expired_at = date('Y-m-d', strtotime("$expired_at +".$paidOrder->goods->days.' days'));
             }
@@ -153,8 +130,8 @@ class OrderService
         ]);
     }
 
-    private function setCommissionExpense(User $user) // 佣金计算
-    {
+    private function setCommissionExpense(User $user)
+    { // 佣金计算
         $referralType = sysConfig('referral_type');
 
         if ($referralType && $user->inviter_id) {// 是否需要支付佣金
@@ -164,7 +141,7 @@ class OrderService
             // 无记录 / 首次返利
             if ($referral && sysConfig('is_invite_register')) {
                 // 邀请注册功能开启时,返还邀请者邀请名额
-                $inviter->update(['invite_num' => $inviter->invite_num + 1]);
+                $inviter->increment('invite_num');
             }
             // 按照返利模式进行返利判断
             if ($referralType === '2' || $referral) {
@@ -181,12 +158,9 @@ class OrderService
         return true;
     }
 
-    public function activatePrepaidPlan(): bool // 激活预支付套餐
-    {
-        self::$order->update([
-            'expired_at' => date('Y-m-d H:i:s', strtotime(self::$goods->days.' days')),
-            'status'     => 2,
-        ]);
+    public function activatePrepaidPlan(): bool
+    { // 激活预支付套餐
+        self::$order->complete();
 
         return $this->activatePlan();
     }

+ 32 - 0
database/migrations/2023_04_22_005731_change_subscribe_desc.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class ChangeSubscribeDesc extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('user_subscribe', function (Blueprint $table) {
+            $table->text('ban_desc')->nullable()->comment('封禁理由')->change();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('user_subscribe', function (Blueprint $table) {
+            $table->string('ban_desc', 50)->nullable()->comment('封禁理由')->change();
+        });
+    }
+}

+ 46 - 0
public/assets/global/js/Plugin/ionrangeslider.js

@@ -0,0 +1,46 @@
+(function (global, factory) {
+  if (typeof define === "function" && define.amd) {
+    define("/Plugin/ionrangeslider", ["exports", "Plugin"], factory);
+  } else if (typeof exports !== "undefined") {
+    factory(exports, require("Plugin"));
+  } else {
+    var mod = {
+      exports: {}
+    };
+    factory(mod.exports, global.Plugin);
+    global.PluginIonrangeslider = mod.exports;
+  }
+})(this, function (_exports, _Plugin2) {
+  "use strict";
+
+  Object.defineProperty(_exports, "__esModule", {
+    value: true
+  });
+  _exports.default = void 0;
+  _Plugin2 = babelHelpers.interopRequireDefault(_Plugin2);
+  var NAME = 'ionRangeSlider';
+
+  var IonRangeSlider =
+  /*#__PURE__*/
+  function (_Plugin) {
+    babelHelpers.inherits(IonRangeSlider, _Plugin);
+
+    function IonRangeSlider() {
+      babelHelpers.classCallCheck(this, IonRangeSlider);
+      return babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(IonRangeSlider).apply(this, arguments));
+    }
+
+    babelHelpers.createClass(IonRangeSlider, [{
+      key: "getName",
+      value: function getName() {
+        return NAME;
+      }
+    }]);
+    return IonRangeSlider;
+  }(_Plugin2.default);
+
+  _Plugin2.default.register(NAME, IonRangeSlider);
+
+  var _default = IonRangeSlider;
+  _exports.default = _default;
+});

+ 1 - 45
public/robots.txt

@@ -1,46 +1,2 @@
-User-agent: Baiduspider
+User-agent: *
 Disallow: /
-User-agent: Sosospider
-Disallow: /
-User-agent: sogou spider
-Disallow: /
-User-agent: YodaoBot
-Disallow: /
-User-agent: Googlebot
-Disallow: /
-User-agent: Bingbot
-Disallow: /
-User-agent: Slurp
-Disallow: /
-User-agent: Teoma
-Disallow: /
-User-agent: ia_archiver
-Disallow: /
-User-agent: twiceler
-Disallow: /
-User-agent: MSNBot
-Disallow: /
-User-agent: Scrubby
-Disallow: /
-User-agent: Robozilla
-Disallow: /
-User-agent: Gigabot
-Disallow: /
-User-agent: googlebot-image
-Disallow: /
-User-agent: googlebot-mobile
-Disallow: /
-User-agent: yahoo-mmcrawler
-Disallow: /
-User-agent: yahoo-blogs/v3.9
-Disallow: /
-User-agent: psbot
-Disallow: /
-Disallow: /public/
-Disallow: /sql/
-Disallow: /config/
-Disallow: /app/
-Disallow: /database/
-Disallow: /resources/
-Disallow: /routes/
-Disallow: /vendor

+ 66 - 52
resources/lang/en.json

@@ -1,54 +1,68 @@
 {
-    "A fresh verification link has been sent to your email address.": "A fresh verification link has been sent to your email address.",
-    "All rights reserved.": "All rights reserved.",
-    "Before proceeding, please check your email for a verification link.": "Before proceeding, please check your email for a verification link.",
-    "click here to request another": "click here to request another",
-    "Confirm Password": "Confirm Password",
-    "E-Mail Address": "E-Mail Address",
-    "Forbidden": "Forbidden",
-    "Forgot Your Password?": "Forgot Your Password?",
-    "Go Home": "Go Home",
-    "Go to page :page": "Go to page :page",
-    "Hello!": "Hello!",
-    "If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.",
-    "If you did not receive the email": "If you did not receive the email",
-    "If you did not request a password reset, no further action is required.": "If you did not request a password reset, no further action is required.",
-    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:",
-    "Login": "Login",
-    "Logout": "Logout",
-    "Name": "Name",
-    "Not Found": "Not Found",
-    "of": "of",
-    "Oh no": "Oh no",
-    "Page Expired": "Page Expired",
-    "Pagination Navigation": "Pagination Navigation",
-    "Password": "Password",
-    "Please click the button below to verify your email address.": "Please click the button below to verify your email address.",
-    "Regards": "Regards",
-    "Register": "Register",
-    "Remember Me": "Remember Me",
-    "Reset Password": "Reset Password",
-    "Reset Password Notification": "Reset Password Notification",
-    "results": "results",
-    "Send Password Reset Link": "Send Password Reset Link",
-    "Server Error": "Server Error",
-    "Service Unavailable": "Service Unavailable",
-    "Showing": "Showing",
-    "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.",
-    "The :attribute must contain at least one letter.": "The :attribute must contain at least one letter.",
-    "The :attribute must contain at least one number.": "The :attribute must contain at least one number.",
-    "The :attribute must contain at least one symbol.": "The :attribute must contain at least one symbol.",
-    "The :attribute must contain at least one uppercase and one lowercase letter.": "The :attribute must contain at least one uppercase and one lowercase letter.",
-    "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "The given :attribute has appeared in a data leak. Please choose a different :attribute.",
-    "This password reset link will expire in :count minutes.": "This password reset link will expire in :count minutes.",
-    "to": "to",
-    "Toggle navigation": "Toggle navigation",
-    "Too Many Requests": "Too Many Requests",
-    "Unauthorized": "Unauthorized",
-    "Verify Email Address": "Verify Email Address",
-    "Verify Your Email Address": "Verify Your Email Address",
-    "Whoops!": "Whoops!",
-    "You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account.",
-    "You have not responded this ticket in :num hours, System has closed your ticket.": "You have not responded this ticket in :num hours, System has closed your ticket.",
-    "You must have a valid subscription to view the content in this area!": "You must have a valid subscription to view the content in this area!"
+  "A fresh verification link has been sent to your email address.": "A fresh verification link has been sent to your email address.",
+  "All rights reserved.": "All rights reserved.",
+  "Before proceeding, please check your email for a verification link.": "Before proceeding, please check your email for a verification link.",
+  "click here to request another": "click here to request another",
+  "Confirm Password": "Confirm Password",
+  "E-Mail Address": "E-Mail Address",
+  "Forbidden": "Forbidden",
+  "Forgot Your Password?": "Forgot Your Password?",
+  "Go Home": "Go Home",
+  "Go to page :page": "Go to page :page",
+  "Hello!": "Hello!",
+  "If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.",
+  "If you did not receive the email": "If you did not receive the email",
+  "If you did not request a password reset, no further action is required.": "If you did not request a password reset, no further action is required.",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:",
+  "Login": "Login",
+  "Logout": "Logout",
+  "Name": "Name",
+  "Not Found": "Not Found",
+  "of": "of",
+  "Oh no": "Oh no",
+  "Page Expired": "Page Expired",
+  "Pagination Navigation": "Pagination Navigation",
+  "Password": "Password",
+  "Please click the button below to verify your email address.": "Please click the button below to verify your email address.",
+  "Regards": "Regards",
+  "Register": "Register",
+  "Remember Me": "Remember Me",
+  "Reset Password": "Reset Password",
+  "Reset Password Notification": "Reset Password Notification",
+  "results": "results",
+  "Send Password Reset Link": "Send Password Reset Link",
+  "Server Error": "Server Error",
+  "Service Unavailable": "Service Unavailable",
+  "Showing": "Showing",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.",
+  "The :attribute must contain at least one letter.": "The :attribute must contain at least one letter.",
+  "The :attribute must contain at least one number.": "The :attribute must contain at least one number.",
+  "The :attribute must contain at least one symbol.": "The :attribute must contain at least one symbol.",
+  "The :attribute must contain at least one uppercase and one lowercase letter.": "The :attribute must contain at least one uppercase and one lowercase letter.",
+  "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "The given :attribute has appeared in a data leak. Please choose a different :attribute.",
+  "This password reset link will expire in :count minutes.": "This password reset link will expire in :count minutes.",
+  "to": "to",
+  "Toggle navigation": "Toggle navigation",
+  "Too Many Requests": "Too Many Requests",
+  "Unauthorized": "Unauthorized",
+  "Verify Email Address": "Verify Email Address",
+  "Verify Your Email Address": "Verify Your Email Address",
+  "Whoops!": "Whoops!",
+  "You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account.",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": "You have not responded this ticket in :num hours, System has closed your ticket.",
+  "You must have a valid subscription to view the content in this area!": "You must have a valid subscription to view the content in this area!",
+  "Daily Data Usage Report": "Daily Data Usage Report",
+  "Invoice Detail": "Invoice Detail",
+  "Payment for #:sn has been received! Total amount: :amount.": "Payment for #:sn has been received! Total amount: :amount.",
+  "Payment Received": "Payment Received",
+  "[Service Timer] Service Expiration": "[Service Timer] Service Expiration",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours",
+  "[Auto Task] Blocked service: Run out of traffic": "[Auto Task] Blocked service: Run out of traffic",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[Auto Task] Blocked service: Abnormal traffic within 1 hour",
+  "[Daily Task] Account Expiration: Stop Service": "[Daily Task] Account Expiration: Stop Service",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[Daily Task] Account Expiration: Block Login & Clear Account",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[Daily Task] Reset Account Traffic, Next Reset Date: :date",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」Completed, Used :time seconds ----",
+  "Subscription link receive abnormal access and banned by the system": "Subscription link receive abnormal access and banned by the system",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "Your subscription has been disabled by the administrator, please contact the administrator to restore it"
 }

+ 12 - 12
resources/lang/en/admin.php

@@ -75,7 +75,7 @@ return [
         'promotion'        => [
             'attribute'   => 'Promotions',
             'invite'      => 'Referral Management',
-            'withdraw'    => 'Commission Withdrawal Management',
+            'withdraw'    => 'Commission Withdrawals',
             'rebate_flow' => 'Rebate History',
         ],
         'analysis'         => [
@@ -610,7 +610,7 @@ return [
         'wechat_qrcode'                 => 'WeChat QrCode',
         'wechat_secret'                 => 'Enterprise WeChat Secret',
         'wechat_token'                  => 'Enterprise WeChat Token',
-        'hint'           => [
+        'hint'                          => [
             'account_expire_notification'   => 'Notify users that their accounts are about to expire',
             'active_times'                  => 'The number of times a user can activate an account via email within 1 day',
             'admin_invite_days'             => 'Expiration date of the invitation code generated by the administrator',
@@ -687,7 +687,7 @@ return [
             'ticket_closed_notification'    => 'Notify user when ticket closed',
             'ticket_created_notification'   => 'Notification of newly created ticket to management/user, depending on whom created',
             'ticket_replied_notification'   => 'Ticket response notification to the other party',
-            'traffic_ban_time'              => 'The duration of the user\'s ban due to traffic abnormalities is triggered and automatically unblocked upon expiry',
+            'traffic_ban_time'              => 'Any user/subscription anomaly blocking time, automatically unblocked upon expiry',
             'traffic_ban_value'             => 'If the value is exceeded within 1 hour, the ban will be triggered',
             'traffic_limit_time'            => 'Check-in Interval',
             'traffic_warning_percent'       => '[Traffic Exhaustion Notification] Start threshold, notify users daily',
@@ -708,7 +708,7 @@ return [
             'wechat_secret'                 => 'App Secret (may need to download enterprise WeChat to be able view it)',
             'wechat_token'                  => 'App Management -> Application -> Set API -> TOKEN, URL set to :url',
         ],
-        'placeholder'    => [
+        'placeholder'                   => [
             'default_url'           => 'Default as :url',
             'server_chan_key'       => 'Fill in ServerChan\'s SCKEY, then press Update',
             'pushDeer_key'          => 'Fill in Push Key, then press Update',
@@ -724,7 +724,7 @@ return [
             'tg_chat_token'         => 'Please apply at Telegram',
             'codepay_url'           => 'https://codepay.fatq.com/create_order/?',
         ],
-        'payment'        => [
+        'payment'                       => [
             'attribute' => 'Payment Gateway',
             'channel'   => [
                 'alipay'    => 'Alipay F2F',
@@ -747,7 +747,7 @@ return [
                 'manual'    => 'After the gateway is set and selected, it will be displayed on the user-end',
             ],
         ],
-        'notification'   => [
+        'notification'                  => [
             'channel'   => [
                 'telegram'   => 'Telegram',
                 'wechat'     => 'Enterprise WeChat',
@@ -763,33 +763,33 @@ return [
             ],
             'send_test' => 'Send Test Message',
         ],
-        'forbid'         => [
+        'forbid'                        => [
             'mainland' => 'Forbid Chinese Mainland Access',
             'china'    => 'Forbid China Access',
             'oversea'  => 'Forbid Oversea Access',
         ],
-        'username'       => [
+        'username'                      => [
             'email'  => 'Email',
             'mobile' => 'Phone number',
             'any'    => 'Any Username',
         ],
-        'active_account' => [
+        'active_account'                => [
             'before' => 'Pre-registration activation',
             'after'  => 'Activate after registration',
         ],
-        'ddns'           => [
+        'ddns'                          => [
             'namesilo'   => 'Namesilo',
             'aliyun'     => 'AliCloud/Aliyun',
             'dnspod'     => 'DNSPod',
             'cloudflare' => 'CloudFlare',
         ],
-        'captcha'        => [
+        'captcha'                       => [
             'standard'  => 'Standard',
             'geetest'   => 'Geetest',
             'recaptcha' => 'Google ReCaptcha',
             'hcaptcha'  => 'hCaptcha',
         ],
-        'referral'       => [
+        'referral'                      => [
             'once' => 'First Purchase Rebate',
             'loop' => 'Always Rebate',
         ],

+ 4 - 4
resources/lang/en/common.php

@@ -1,7 +1,7 @@
 <?php
 
 return [
-    'hour'            => '{1} Hour | {2} Clock',
+    'hour'            => '{1} Hour | {2} O\'clock',
     'account'         => 'Account',
     'available_date'  => 'Available Until/Period',
     'created_at'      => 'Created At',
@@ -61,7 +61,7 @@ return [
         'fullscreen' => 'Fullscreen',
     ],
     'days'            => [
-        'attribute' => '{1} Day | Day {2}',
+        'attribute' => '{1} Days | Day {2}',
         'sun'       => 'Sunday',
         'mon'       => 'Monday',
         'tue'       => 'Tuesday',
@@ -86,7 +86,7 @@ return [
         'crypto' => 'Crypto',
         'manual' => 'Manual Payment',
         'status' => [
-            'wait'    => 'Awaiting payment',
+            'wait' => 'Awaiting payment',
         ],
     ],
     'order'           => [
@@ -122,7 +122,7 @@ return [
         'unwithdrawn'       => 'Not withdrawn',
         'reply'             => 'Replied',
         'pending'           => 'Pending',
-        'unknown'           => 'Announcements',
+        'unknown'           => 'Unknown',
         'available'         => 'Active',
         'reject'            => 'Reject',
         'rejected'          => 'Rejected',

+ 3 - 3
resources/lang/en/model.php

@@ -217,9 +217,9 @@ return [
         'log_time' => 'Record Time',
     ],
     'user_data_modify' => [
-        'before'      => 'Pre-change Data',
-        'after'       => 'Data after change',
-        'created_at'  => 'Operation Date/Time',
+        'before'     => 'Pre-change Data',
+        'after'      => 'Data after change',
+        'created_at' => 'Operation Date/Time',
     ],
     'user_credit'      => [
         'before'     => 'Balance before operation',

+ 1 - 1
resources/lang/en/notification.php

@@ -4,7 +4,7 @@ return [
     'attribute'               => 'NOTIFICATIONS',
     'new'                     => ':num new message|:num new messages',
     'empty'                   => 'No new message',
-    'payment_received'        => 'Order has paid, Amount: :amount! Click me to view detail',
+    'payment_received'        => 'Your order (Amount: :amount) is successfully paid. Click to view details',
     'account_expired'         => 'Account Going to Expire',
     'account_expired_content' => 'Your account will be expired after [:days] days. For your server experience, please renew your account ahead of time.',
     'account_expired_blade'   => 'Account will be expired in [:day] days, Please renew it in time',

+ 1 - 2
resources/lang/en/user.php

@@ -32,7 +32,7 @@ return [
         'announcement'       => 'Announcements',
         'wechat_push'        => 'WeChat Notification Service',
         'chat_group'         => 'Chat Group',
-        'empty_announcement' => 'No Announcement',
+        'empty_announcement' => 'No Announcements',
     ],
     'purchase_to_unlock'  => 'Unlock after purchasing service',
     'purchase_required'   => 'This feature is disabled for non-paying users! Please',
@@ -171,7 +171,6 @@ return [
         'rate'     => ':ratio time data consumption',
     ],
     'subscribe'           => [
-        'baned'            => 'Your subscription function is disabled, please contact the administrator to restore',
         'link'             => 'Subscription Link',
         'tips'             => 'Warning: Subscribe Link is for personal used only, Please do not show to anyone else. Otherwise, they may use your service without your permission',
         'exchange_warning' => 'Exchange Link:\n1. Old Link will be disabled\n2. Proxy connection password will be reset',

+ 66 - 52
resources/lang/ja.json

@@ -1,54 +1,68 @@
 {
-    "A fresh verification link has been sent to your email address.": "新しい確認リンクがメールアドレスに送信されました。",
-    "All rights reserved.": "All rights reserved.",
-    "Before proceeding, please check your email for a verification link.": "先に進む前に、確認リンクのためにあなたの電子メールを確認してください。",
-    "click here to request another": "ここをクリックして別のものをリクエストしてください",
-    "Confirm Password": "パスワード(確認用)",
-    "E-Mail Address": "電子メールアドレス",
-    "Forbidden": "禁止されています",
-    "Forgot Your Password?": "パスワードを忘れた方はこちら",
-    "Go Home": "ホームへ",
-    "Go to page :page": ":pageページへ",
-    "Hello!": "こんにちは",
-    "If you did not create an account, no further action is required.": "アカウントの作成にお心当たりがない場合は、このメールを無視してください。",
-    "If you did not receive the email": "メールが受信できなかった場合",
-    "If you did not request a password reset, no further action is required.": "パスワード再設定のリクエストにお心当たりがない場合は、このメールを無視してください。",
-    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "\":actionText\"ボタンがクリックできない場合は以下のURLを直接入力してください。",
-    "Login": "ログイン",
-    "Logout": "ログアウト",
-    "Name": "氏名",
-    "Not Found": "見つかりません",
-    "of": "の",
-    "Oh no": "オーノー",
-    "Page Expired": "ページが無効です",
-    "Pagination Navigation": "ページネーション",
-    "Password": "パスワード",
-    "Please click the button below to verify your email address.": "メールアドレスを確認するには、以下のボタンをクリックしてください。",
-    "Regards": "よろしくお願いします",
-    "Register": "アカウント作成",
-    "Remember Me": "ログイン状態を保持する",
-    "Reset Password": "パスワード再設定",
-    "Reset Password Notification": "パスワード再設定のお知らせ",
-    "results": "結果",
-    "Send Password Reset Link": "パスワード再設定URLを送信",
-    "Server Error": "サーバーエラー",
-    "Service Unavailable": "サービスは利用できません",
-    "Showing": "示すこと",
-    "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "ご登録ありがとうございます! メールアドレスを確認するため、あなたのメールボックスに送信リンクをクリックしてあなたのメールを送信してください。もしメールが届かない場合は再度メールを送信してください。",
-    "The :attribute must contain at least one letter.": ":attributeは文字を1文字以上含めなければなりません。",
-    "The :attribute must contain at least one number.": ":attributeは数字を1文字以上含めなければなりません。",
-    "The :attribute must contain at least one symbol.": ":attributeは記号を1文字以上含めなければなりません。",
-    "The :attribute must contain at least one uppercase and one lowercase letter.": ":attributeは大文字と小文字をそれぞれ1文字以上含めなければなりません。",
-    "The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":attributeはデータ漏洩の対象だった可能性があります。別の:attributeを選んでください。",
-    "This password reset link will expire in :count minutes.": "このパスワード再設定リンクの有効期限は:count分です。",
-    "to": "に",
-    "Toggle navigation": "ナビゲーションを切り替える",
-    "Too Many Requests": "リクエストが多すぎます",
-    "Unauthorized": "認証が必要です",
-    "Verify Email Address": "メールアドレスを確認してください",
-    "Verify Your Email Address": "メールアドレスの確認",
-    "Whoops!": "おっと!",
-    "You are receiving this email because we received a password reset request for your account.": "パスワード再設定のリクエストを受け付けました。",
-    "You have not responded this ticket in :num hours, System has closed your ticket.": "超:num以上返信のチケットは報告されていません。自動的にチケットのプロセスを停止しました。",
-    "You must have a valid subscription to view the content in this area!": "この地域のコンテンツを表示するには、利用可能なプランが必要です。"
+  "A fresh verification link has been sent to your email address.": "新しい確認リンクがメールアドレスに送信されました。",
+  "All rights reserved.": "All rights reserved.",
+  "Before proceeding, please check your email for a verification link.": "先に進む前に、確認リンクのためにあなたの電子メールを確認してください。",
+  "click here to request another": "ここをクリックして別のものをリクエストしてください",
+  "Confirm Password": "パスワード(確認用)",
+  "E-Mail Address": "電子メールアドレス",
+  "Forbidden": "禁止されています",
+  "Forgot Your Password?": "パスワードを忘れた方はこちら",
+  "Go Home": "ホームへ",
+  "Go to page :page": ":pageページへ",
+  "Hello!": "こんにちは",
+  "If you did not create an account, no further action is required.": "アカウントの作成にお心当たりがない場合は、このメールを無視してください。",
+  "If you did not receive the email": "メールが受信できなかった場合",
+  "If you did not request a password reset, no further action is required.": "パスワード再設定のリクエストにお心当たりがない場合は、このメールを無視してください。",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "\":actionText\"ボタンがクリックできない場合は以下のURLを直接入力してください。",
+  "Login": "ログイン",
+  "Logout": "ログアウト",
+  "Name": "氏名",
+  "Not Found": "見つかりません",
+  "of": "の",
+  "Oh no": "オーノー",
+  "Page Expired": "ページが無効です",
+  "Pagination Navigation": "ページネーション",
+  "Password": "パスワード",
+  "Please click the button below to verify your email address.": "メールアドレスを確認するには、以下のボタンをクリックしてください。",
+  "Regards": "よろしくお願いします",
+  "Register": "アカウント作成",
+  "Remember Me": "ログイン状態を保持する",
+  "Reset Password": "パスワード再設定",
+  "Reset Password Notification": "パスワード再設定のお知らせ",
+  "results": "結果",
+  "Send Password Reset Link": "パスワード再設定URLを送信",
+  "Server Error": "サーバーエラー",
+  "Service Unavailable": "サービスは利用できません",
+  "Showing": "示すこと",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "ご登録ありがとうございます! メールアドレスを確認するため、あなたのメールボックスに送信リンクをクリックしてあなたのメールを送信してください。もしメールが届かない場合は再度メールを送信してください。",
+  "The :attribute must contain at least one letter.": ":attributeは文字を1文字以上含めなければなりません。",
+  "The :attribute must contain at least one number.": ":attributeは数字を1文字以上含めなければなりません。",
+  "The :attribute must contain at least one symbol.": ":attributeは記号を1文字以上含めなければなりません。",
+  "The :attribute must contain at least one uppercase and one lowercase letter.": ":attributeは大文字と小文字をそれぞれ1文字以上含めなければなりません。",
+  "The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":attributeはデータ漏洩の対象だった可能性があります。別の:attributeを選んでください。",
+  "This password reset link will expire in :count minutes.": "このパスワード再設定リンクの有効期限は:count分です。",
+  "to": "に",
+  "Toggle navigation": "ナビゲーションを切り替える",
+  "Too Many Requests": "リクエストが多すぎます",
+  "Unauthorized": "認証が必要です",
+  "Verify Email Address": "メールアドレスを確認してください",
+  "Verify Your Email Address": "メールアドレスの確認",
+  "Whoops!": "おっと!",
+  "You are receiving this email because we received a password reset request for your account.": "パスワード再設定のリクエストを受け付けました。",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": "超:num以上返信のチケットは報告されていません。自動的にチケットのプロセスを停止しました。",
+  "You must have a valid subscription to view the content in this area!": "この地域のコンテンツを表示するには、利用可能なプランが必要です。",
+  "Daily Data Usage Report": "回線毎日のトラフィックレポート",
+  "Invoice Detail": "注文詳細",
+  "Payment for #:sn has been received! Total amount: :amount.": "ご注文を支払うことに成功しました。合計金額は :amountです。",
+  "Payment Received": "請求書の支払いが完了しました",
+  "[Service Timer] Service Expiration": "[定时任务]有効期限が過ぎています",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[自动任务]サブスクリプションの購読:24時間以内に有効な例外を出してください",
+  "[Auto Task] Blocked service: Run out of traffic": "[自动任务]サービスをBAN: 空き容量",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[自动任务]サービスをブロック: 1時間以内に通信例外",
+  "[Daily Task] Account Expiration: Stop Service": "[每日任务]アカウントの有効期限が切れています",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[每日任务]アカウントの有効期限が切れている、ログイン禁止、アカウントが消去されます",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "'[每日任务]アカウントのリセット、次のリセット日: :date",
+  "----「:job」Completed, Used :time seconds ----": "----:job.時間 :time 秒----",
+  "Subscription link receive abnormal access and banned by the system": "自動アクセス例外を購読する",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "購読は無効になっています。回復するには管理者に連絡してください。"
 }

+ 10 - 10
resources/lang/ja/admin.php

@@ -611,7 +611,7 @@ return [
         'wechat_qrcode'                 => 'やみのQRコード',
         'wechat_secret'                 => 'WeChat アプリキー',
         'wechat_token'                  => 'WeChat AppEN',
-        'hint'           => [
+        'hint'                          => [
             'account_expire_notification'   => 'ユーザーに通知して締め切られます',
             'active_times'                  => '24時間、メールからアカウントを使用した回数',
             'admin_invite_days'             => '管理者により招待コードを生成する有効期間',
@@ -710,7 +710,7 @@ return [
             'wechat_secret'                 => 'アプリによるSecret(閲覧するにはエンタープライズ版をダウンロードする必要あり)あります',
             'wechat_token'                  => 'アプリマネージャー - アプリ->APIの受信->TOKEN、URLを設定::url',
         ],
-        'placeholder'    => [
+        'placeholder'                   => [
             'default_url'           => 'デフォルトは :url',
             'server_chan_key'       => 'サーバーChant の「SCKEY-」ボタンを押す',
             'pushDeer_key'          => 'プッシュ型付きのPush Key ->更新をタップ',
@@ -726,7 +726,7 @@ return [
             'tg_chat_token'         => 'Telegram にアクセスする',
             'codepay_url'           => 'https://codepay.fateq.com/creat_order/?',
         ],
-        'payment'        => [
+        'payment'                       => [
             'attribute' => '支払い設定',
             'channel'   => [
                 'alipay'    => 'PayPalF2F',
@@ -749,7 +749,7 @@ return [
                 'manual'    => '対応する画面を表示するように変更する',
             ],
         ],
-        'notification'   => [
+        'notification'                  => [
             'channel'   => [
                 'telegram'   => 'TGテレックス',
                 'wechat'     => 'マイクロ信企業',
@@ -765,33 +765,33 @@ return [
             ],
             'send_test' => 'テストメールの送信',
         ],
-        'forbid'         => [
+        'forbid'                        => [
             'mainland' => '連合の排除',
             'china'    => '中国をブロックする',
             'oversea'  => 'ブロックを消す',
         ],
-        'username'       => [
+        'username'                      => [
             'email'  => 'メールアドレス',
             'mobile' => '携帯電話番号',
             'any'    => 'ユーザー名',
         ],
-        'active_account' => [
+        'active_account'                => [
             'before' => '登録する前にアクティブにする',
             'after'  => '登録後にアクティブにする',
         ],
-        'ddns'           => [
+        'ddns'                          => [
             'namesilo'   => 'Namesiilo',
             'aliyun'     => 'Aliyun (国際&国内)',
             'dnspod'     => 'DNS Pod',
             'cloudflare' => 'CloudFlarare',
         ],
-        'captcha'        => [
+        'captcha'                       => [
             'standard'  => '平均サイズ',
             'geetest'   => 'ベストプラス',
             'recaptcha' => 'Google reCaptcha',
             'hcaptcha'  => 'hCaptcha',
         ],
-        'referral'       => [
+        'referral'                      => [
             'once' => '初回購入',
             'loop' => 'ループ還元',
         ],

+ 1 - 1
resources/lang/ja/common.php

@@ -86,7 +86,7 @@ return [
         'crypto' => 'バーチャル通貨',
         'manual' => '自動決済',
         'status' => [
-            'wait'    => '未支払',
+            'wait' => '未支払',
         ],
     ],
     'order'           => [

+ 3 - 3
resources/lang/ja/model.php

@@ -217,9 +217,9 @@ return [
         'log_time' => '記録時間',
     ],
     'user_data_modify' => [
-        'before'      => '変更前のトラフィック',
-        'after'       => '運用の後退率',
-        'created_at'  => 'タイムスタンプ',
+        'before'     => '変更前のトラフィック',
+        'after'      => '運用の後退率',
+        'created_at' => 'タイムスタンプ',
     ],
     'user_credit'      => [
         'before'     => '操作前残高',

+ 0 - 1
resources/lang/ja/user.php

@@ -171,7 +171,6 @@ return [
         'rate'     => ':ratio倍トラフィック消費',
     ],
     'subscribe'           => [
-        'baned'            => '登録機能は無効になっています。回復するには管理者に連絡してください。',
         'link'             => 'サブスクリプションリンク',
         'tips'             => 'アラート:このサブスクリプションリンクは個人にのみ利用され、このリンクを伝染しない限り、そのリンクはアカウントの利用量の異常なパケットを発信します。',
         'exchange_warning' => 'サブスクリプションアドレスを変更すると、\n1.古いアドレスがすぐ無効\n2.接続パスワードの変更が発生します。',

+ 66 - 52
resources/lang/ko.json

@@ -1,54 +1,68 @@
 {
-    "A fresh verification link has been sent to your email address.": "새로운 인증 링크를 이메일로 보냈습니다.",
-    "All rights reserved.": "모든 권리 보유.",
-    "Before proceeding, please check your email for a verification link.": "진행하기 전에 이메일에 인증 링크를 확인하십시오.",
-    "click here to request another": "다른 항목을 요청하려면 여기를 클릭하십시오.",
-    "Confirm Password": "비밀번호 확인",
-    "E-Mail Address": "E-Mail Address",
-    "Forbidden": "권한 없음",
-    "Forgot Your Password?": "비밀번호를 잊으셨나요?",
-    "Go Home": "홈으로 이동",
-    "Go to page :page": ":page 페이지로 이동",
-    "Hello!": "안녕하세요!",
-    "If you did not create an account, no further action is required.": "계정을 생성하지 않았다면 추가 조치가 필요하지 않습니다.",
-    "If you did not receive the email": "이메일을 받지 못하셨다면",
-    "If you did not request a password reset, no further action is required.": "귀하께서 비밀번호 재설정을 요청하지 않으셨다면, 추가 조치가 필요하지 않습니다.",
-    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "\":actionText\" 버튼을 클릭하는 중에 문제가 있는 경우 아래 URL을 복사하여\n웹 브라우저에 붙여넣으세요:",
-    "Login": "로그인",
-    "Logout": "로그아웃",
-    "Name": "이름",
-    "Not Found": "찾을 수 없습니다",
-    "of": "의",
-    "Oh no": "저런!",
-    "Page Expired": "만료된 페이지",
-    "Pagination Navigation": "페이지네이션 내비게이션",
-    "Password": "비밀번호",
-    "Please click the button below to verify your email address.": "이메일 주소를 확인하려면 아래 버튼을 클릭하십시오.",
-    "Regards": "안부",
-    "Register": "회원가입",
-    "Remember Me": "로그인 상태 유지",
-    "Reset Password": "비밀번호 재설정",
-    "Reset Password Notification": "비밀번호 재설정 알림 ",
-    "results": "결과",
-    "Send Password Reset Link": "비밀번호 재설정 링크 보내기",
-    "Server Error": "서버 오류",
-    "Service Unavailable": "서비스를 사용할 수 없습니다.",
-    "Showing": "보기",
-    "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "회원가입 해주셔서 감사합니다! 시작하기 전에 이메일 계정으로 보낸 링크를 클릭해서 이메일을 확인해야 합니다! 만약 이메일을 받지 못했다면, 다시 전송해드리겠습니다.",
-    "The :attribute must contain at least one letter.": ":attribute은(는) 반드시 하나 이상의 문자를 포함해야합니다.",
-    "The :attribute must contain at least one number.": ":attribute은(는) 반드시 하나 이상의 숫자를 포함해야합니다.",
-    "The :attribute must contain at least one symbol.": "attribute은(는) 반드시 하나 이상의 특수 문자를 포함해야합니다.",
-    "The :attribute must contain at least one uppercase and one lowercase letter.": ":attribute에는 하나 이상의 대문자와 하나의 소문자가 포함되어야 합니다.",
-    "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "주어진 :attribute이(가) 데이터 누출에 나타났습니다. 다른 :attribute을(를) 선택하세요.",
-    "This password reset link will expire in :count minutes.": "이 비밀번호 재설정 링크는 :count분 후에 만료됩니다.",
-    "to": "에",
-    "Toggle navigation": "내비게이션 전환",
-    "Too Many Requests": "너무 많은 요청",
-    "Unauthorized": "인증되지 않음",
-    "Verify Email Address": "이메일 주소 확인",
-    "Verify Your Email Address": "이메일 주소 확인",
-    "Whoops!": "저런!",
-    "You are receiving this email because we received a password reset request for your account.": "귀하의 계정에 대한 비밀번호 재설정이 요청되어 이 메일이 발송되었습니다.",
-    "You have not responded this ticket in :num hours, System has closed your ticket.": ":num시간 이상 동안 문의 답장이 없습니다. 시스템이 자동으로 문의를 닫았습니다.",
-    "You must have a valid subscription to view the content in this area!": "이 영역의 콘텐츠를 보려면 유효한 패키지가 있어야 합니다!"
+  "A fresh verification link has been sent to your email address.": "새로운 인증 링크를 이메일로 보냈습니다.",
+  "All rights reserved.": "모든 권리 보유.",
+  "Before proceeding, please check your email for a verification link.": "진행하기 전에 이메일에 인증 링크를 확인하십시오.",
+  "click here to request another": "다른 항목을 요청하려면 여기를 클릭하십시오.",
+  "Confirm Password": "비밀번호 확인",
+  "E-Mail Address": "E-Mail Address",
+  "Forbidden": "권한 없음",
+  "Forgot Your Password?": "비밀번호를 잊으셨나요?",
+  "Go Home": "홈으로 이동",
+  "Go to page :page": ":page 페이지로 이동",
+  "Hello!": "안녕하세요!",
+  "If you did not create an account, no further action is required.": "계정을 생성하지 않았다면 추가 조치가 필요하지 않습니다.",
+  "If you did not receive the email": "이메일을 받지 못하셨다면",
+  "If you did not request a password reset, no further action is required.": "귀하께서 비밀번호 재설정을 요청하지 않으셨다면, 추가 조치가 필요하지 않습니다.",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "\":actionText\" 버튼을 클릭하는 중에 문제가 있는 경우 아래 URL을 복사하여\n웹 브라우저에 붙여넣으세요:",
+  "Login": "로그인",
+  "Logout": "로그아웃",
+  "Name": "이름",
+  "Not Found": "찾을 수 없습니다",
+  "of": "의",
+  "Oh no": "저런!",
+  "Page Expired": "만료된 페이지",
+  "Pagination Navigation": "페이지네이션 내비게이션",
+  "Password": "비밀번호",
+  "Please click the button below to verify your email address.": "이메일 주소를 확인하려면 아래 버튼을 클릭하십시오.",
+  "Regards": "안부",
+  "Register": "회원가입",
+  "Remember Me": "로그인 상태 유지",
+  "Reset Password": "비밀번호 재설정",
+  "Reset Password Notification": "비밀번호 재설정 알림 ",
+  "results": "결과",
+  "Send Password Reset Link": "비밀번호 재설정 링크 보내기",
+  "Server Error": "서버 오류",
+  "Service Unavailable": "서비스를 사용할 수 없습니다.",
+  "Showing": "보기",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "회원가입 해주셔서 감사합니다! 시작하기 전에 이메일 계정으로 보낸 링크를 클릭해서 이메일을 확인해야 합니다! 만약 이메일을 받지 못했다면, 다시 전송해드리겠습니다.",
+  "The :attribute must contain at least one letter.": ":attribute은(는) 반드시 하나 이상의 문자를 포함해야합니다.",
+  "The :attribute must contain at least one number.": ":attribute은(는) 반드시 하나 이상의 숫자를 포함해야합니다.",
+  "The :attribute must contain at least one symbol.": "attribute은(는) 반드시 하나 이상의 특수 문자를 포함해야합니다.",
+  "The :attribute must contain at least one uppercase and one lowercase letter.": ":attribute에는 하나 이상의 대문자와 하나의 소문자가 포함되어야 합니다.",
+  "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "주어진 :attribute이(가) 데이터 누출에 나타났습니다. 다른 :attribute을(를) 선택하세요.",
+  "This password reset link will expire in :count minutes.": "이 비밀번호 재설정 링크는 :count분 후에 만료됩니다.",
+  "to": "에",
+  "Toggle navigation": "내비게이션 전환",
+  "Too Many Requests": "너무 많은 요청",
+  "Unauthorized": "인증되지 않음",
+  "Verify Email Address": "이메일 주소 확인",
+  "Verify Your Email Address": "이메일 주소 확인",
+  "Whoops!": "저런!",
+  "You are receiving this email because we received a password reset request for your account.": "귀하의 계정에 대한 비밀번호 재설정이 요청되어 이 메일이 발송되었습니다.",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": ":num시간 이상 동안 문의 답장이 없습니다. 시스템이 자동으로 문의를 닫았습니다.",
+  "You must have a valid subscription to view the content in this area!": "이 영역의 콘텐츠를 보려면 유효한 패키지가 있어야 합니다!",
+  "Daily Data Usage Report": "회선 데이터 보고",
+  "Invoice Detail": "주문서 상세 내역",
+  "Payment for #:sn has been received! Total amount: :amount.": "您成功支付了订单#:sn, 总金额为 :amount.",
+  "Payment Received": "주문서 결제 성공",
+  "[Service Timer] Service Expiration": "[定时任务]服务到期",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[自动任务]封禁订阅: 订阅24小时内请求异常",
+  "[Auto Task] Blocked service: Run out of traffic": "[自动任务]封禁服务: 流量耗尽",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[自动任务]封禁服务: 1小时内流量异常",
+  "[Daily Task] Account Expiration: Stop Service": "[每日任务]账号过期: 停止服务",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[每日任务]账号过期: 禁止登录, 清空账户",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[每日任务]重置账号流量, 下次重置日: :date",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」完成, 耗时 :time 秒----",
+  "Subscription link receive abnormal access and banned by the system": "订阅链接访问异常,系统自动封禁",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "订阅功能被禁用,请联系管理员恢复"
 }

+ 11 - 11
resources/lang/ko/admin.php

@@ -610,7 +610,7 @@ return [
         'wechat_qrcode'                 => '微 信二维码',
         'wechat_secret'                 => '微信企业应用密钥',
         'wechat_token'                  => '微信企业应用TOKEN',
-        'hint'           => [
+        'hint'                          => [
             'account_expire_notification'   => '通知用户账号即将到期',
             'active_times'                  => '24小时内可以通过邮件激活账号次数',
             'admin_invite_days'             => '管理员生成邀请码的有效期',
@@ -687,7 +687,7 @@ return [
             'ticket_closed_notification'    => '工单关闭通知用户',
             'ticket_created_notification'   => '新工单通知管理/用户,取决于谁创建了新工单',
             'ticket_replied_notification'   => '工单回复通知对方',
-            'traffic_ban_time'              => '触发流量异常导致用户被封禁的时长,到期后自动解封',
+            'traffic_ban_time'              => '任何异常导致用户/订阅被封禁的时长, 到期后自动解封',
             'traffic_ban_value'             => '1小时内超过该值,则触发自动封号',
             'traffic_limit_time'            => '间隔多久才可以再次签到',
             'traffic_warning_percent'       => '【流量耗尽通知】开始阈值,每日通知用户',
@@ -708,7 +708,7 @@ return [
             'wechat_secret'                 => '应用的Secret(可能需要下载企业微信才能查看)',
             'wechat_token'                  => '应用管理->应用->设置API接收->TOKEN,URL设置::url',
         ],
-        'placeholder'    => [
+        'placeholder'                   => [
             'default_url'           => '默认为 :url',
             'server_chan_key'       => '填入ServerChan的SCKEY->再点击更新',
             'pushDeer_key'          => '填入PushDeer的Push Key -> 再点击更新',
@@ -724,7 +724,7 @@ return [
             'tg_chat_token'         => '请到Telegram申请',
             'codepay_url'           => 'https://codepay.fateqq.com/creat_order/?',
         ],
-        'payment'        => [
+        'payment'                       => [
             'attribute' => '支付设置',
             'channel'   => [
                 'alipay'    => '支付宝F2F',
@@ -747,7 +747,7 @@ return [
                 'manual'    => '设置后会自动开启对应显示',
             ],
         ],
-        'notification'   => [
+        'notification'                  => [
             'channel'   => [
                 'telegram'   => 'TG电报',
                 'wechat'     => '微信企业',
@@ -763,33 +763,33 @@ return [
             ],
             'send_test' => '发送测试消息',
         ],
-        'forbid'         => [
+        'forbid'                        => [
             'mainland' => '阻拦大陆',
             'china'    => '阻拦中国',
             'oversea'  => '阻拦海外',
         ],
-        'username'       => [
+        'username'                      => [
             'email'  => '电子邮箱',
             'mobile' => '手机号码',
             'any'    => '任意用户名',
         ],
-        'active_account' => [
+        'active_account'                => [
             'before' => '注册前激活',
             'after'  => '注册后激活',
         ],
-        'ddns'           => [
+        'ddns'                          => [
             'namesilo'   => 'Namesilo',
             'aliyun'     => '阿里云(国际&国内)',
             'dnspod'     => 'DNSPod',
             'cloudflare' => 'CloudFlare',
         ],
-        'captcha'        => [
+        'captcha'                       => [
             'standard'  => '普通验证码',
             'geetest'   => '极 验',
             'recaptcha' => 'Google reCaptcha',
             'hcaptcha'  => 'hCaptcha',
         ],
-        'referral'       => [
+        'referral'                      => [
             'once' => '首购返利',
             'loop' => '循环返利',
         ],

+ 2 - 2
resources/lang/ko/common.php

@@ -86,7 +86,7 @@ return [
         'crypto' => '虚拟货币',
         'manual' => '人工支付',
         'status' => [
-            'wait'    => '待支付',
+            'wait' => '待支付',
         ],
     ],
     'order'           => [
@@ -124,7 +124,7 @@ return [
         'pending'           => '대기 중',
         'unknown'           => '未 知',
         'available'         => '生效中',
-        'reject'            => '驳 回',
+        'reject'            => '否 決',
         'rejected'          => '已驳回',
         'review'            => '待审核',
         'reviewed'          => '审核通过待打款',

+ 3 - 3
resources/lang/ko/model.php

@@ -217,9 +217,9 @@ return [
         'log_time' => '记录时间',
     ],
     'user_data_modify' => [
-        'before'      => '变动前流量',
-        'after'       => '变动后流量',
-        'created_at'  => '发生时间',
+        'before'     => '变动前流量',
+        'after'      => '变动后流量',
+        'created_at' => '发生时间',
     ],
     'user_credit'      => [
         'before'     => '操作前余额',

+ 0 - 1
resources/lang/ko/user.php

@@ -171,7 +171,6 @@ return [
         'rate'     => ':ratio배 데이터 소비',
     ],
     'subscribe'           => [
-        'baned'            => '구독 기능이 비활성화되었습니다. 관리자에게 연락하세요.',
         'link'             => '구독 주소',
         'tips'             => '경고: 이 구독 링크는 개인에 한해서만 사용할 수 있습니다. 이 링크를 공유하지 마십시오. 그렇지 않으면 계정에 문제가 생길 수 있습니다.',
         'exchange_warning' => '구독 주소를 변경 시 다음과 같습니다. \n1. 이전 주소는 즉시 비활성화됩니다 \n2. 연결 비밀번호가 변경됩니다.',

+ 66 - 52
resources/lang/vi.json

@@ -1,54 +1,68 @@
 {
-    "A fresh verification link has been sent to your email address.": "Một liên kết xác minh mới đã được gửi đến địa chỉ email của bạn.",
-    "All rights reserved.": "Đã đăng kí bản quyền",
-    "Before proceeding, please check your email for a verification link.": "Trước khi tiếp tục, vui lòng kiểm tra email của bạn để biết liên kết xác minh.",
-    "click here to request another": "click vào đây để tạo yêu cầu khác",
-    "Confirm Password": "Xác Nhận Mật Khẩu",
-    "E-Mail Address": "Địa chỉ E-Mail",
-    "Forbidden": "Cấm Truy Cập",
-    "Forgot Your Password?": "Quên Mật Khẩu?",
-    "Go Home": "Về trang chủ",
-    "Go to page :page": "Tới trang :page",
-    "Hello!": "Xin chào!",
-    "If you did not create an account, no further action is required.": "Nếu bạn không đăng ký tài khoản này, bạn không cần thực hiện thêm hành động nào.",
-    "If you did not receive the email": "Nếu bạn không nhận được email",
-    "If you did not request a password reset, no further action is required.": "Nếu bạn không yêu cầu đặt lại mật khẩu, bạn không cần thực hiện thêm hành động nào.",
-    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Nếu bạn gặp vấn đề khi click vào nút \":actionText\", hãy sao chép dán địa chỉ bên dưới\nvào trình duyệt web của bạn:",
-    "Login": "Đăng nhập",
-    "Logout": "Đăng xuất",
-    "Name": "Tên",
-    "Not Found": "Không Tìm Thấy",
-    "of": "trong",
-    "Oh no": "Oh, không",
-    "Page Expired": "Trang Đã Hết Hạn",
-    "Pagination Navigation": "Điều hướng phân trang",
-    "Password": "Mật khẩu",
-    "Please click the button below to verify your email address.": "Vui lòng click vào nút bên dưới để xác minh địa chỉ email của bạn.",
-    "Regards": "Trân trọng",
-    "Register": "Đăng ký",
-    "Remember Me": "Ghi Nhớ",
-    "Reset Password": "Đặt Lại Mật Khẩu",
-    "Reset Password Notification": "Thông Báo Đặt Lại Mật Khẩu",
-    "results": "kết quả",
-    "Send Password Reset Link": "Gửi Đường Dẫn Đặt Lại Mật Khẩu",
-    "Server Error": "Máy Chủ Gặp Sự Cố",
-    "Service Unavailable": "Dịch Vụ Không Khả Dụng",
-    "Showing": "Đang hiển thị",
-    "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "感谢您的注册! 在开始之前,您需要通过点击我们刚刚发送至您邮箱中的链接来验证您的电子邮件! 如果您没有收到电子邮件,我们很乐意再发送一封给您。",
-    "The :attribute must contain at least one letter.": "Trường :attribute phải chứa ít nhất một chữ cái.",
-    "The :attribute must contain at least one number.": "Trường :attribute phải chứa ít nhất một số.",
-    "The :attribute must contain at least one symbol.": "Trường :attribute must phải chứa ít nhất một ký hiệu.",
-    "The :attribute must contain at least one uppercase and one lowercase letter.": "Trường :attribute phải chứa ít nhất một chữ hoa và một chữ thường.",
-    "The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":attribute đã cho đã xuất hiện trong một vụ rò rỉ dữ liệu. Vui lòng chọn :attribute khác.",
-    "This password reset link will expire in :count minutes.": "Đường dẫn lấy lại mật khẩu sẽ hết hạn trong :count phút.",
-    "to": "tới",
-    "Toggle navigation": "Chuyển hướng điều hướng",
-    "Too Many Requests": "Quá Nhiều Yêu Cầu",
-    "Unauthorized": "Không Được Phép",
-    "Verify Email Address": "Xác Minh Địa Chỉ Email",
-    "Verify Your Email Address": "Xác Minh Địa Chỉ Email Của Bạn",
-    "Whoops!": "Rất tiếc!",
-    "You are receiving this email because we received a password reset request for your account.": "Bạn nhận được email này vì chúng tôi đã nhận được yêu cầu đặt lại mật khẩu cho tài khoản của bạn.",
-    "You have not responded this ticket in :num hours, System has closed your ticket.": "超过:num小时未回复工单,系统已自动关闭了您的工单",
-    "You must have a valid subscription to view the content in this area!": "您必须拥有有效套餐才可以查看该区域的内容!"
+  "A fresh verification link has been sent to your email address.": "Một liên kết xác minh mới đã được gửi đến địa chỉ email của bạn.",
+  "All rights reserved.": "Đã đăng kí bản quyền",
+  "Before proceeding, please check your email for a verification link.": "Trước khi tiếp tục, vui lòng kiểm tra email của bạn để biết liên kết xác minh.",
+  "click here to request another": "click vào đây để tạo yêu cầu khác",
+  "Confirm Password": "Xác Nhận Mật Khẩu",
+  "E-Mail Address": "Địa chỉ E-Mail",
+  "Forbidden": "Cấm Truy Cập",
+  "Forgot Your Password?": "Quên Mật Khẩu?",
+  "Go Home": "Về trang chủ",
+  "Go to page :page": "Tới trang :page",
+  "Hello!": "Xin chào!",
+  "If you did not create an account, no further action is required.": "Nếu bạn không đăng ký tài khoản này, bạn không cần thực hiện thêm hành động nào.",
+  "If you did not receive the email": "Nếu bạn không nhận được email",
+  "If you did not request a password reset, no further action is required.": "Nếu bạn không yêu cầu đặt lại mật khẩu, bạn không cần thực hiện thêm hành động nào.",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Nếu bạn gặp vấn đề khi click vào nút \":actionText\", hãy sao chép dán địa chỉ bên dưới\nvào trình duyệt web của bạn:",
+  "Login": "Đăng nhập",
+  "Logout": "Đăng xuất",
+  "Name": "Tên",
+  "Not Found": "Không Tìm Thấy",
+  "of": "trong",
+  "Oh no": "Oh, không",
+  "Page Expired": "Trang Đã Hết Hạn",
+  "Pagination Navigation": "Điều hướng phân trang",
+  "Password": "Mật khẩu",
+  "Please click the button below to verify your email address.": "Vui lòng click vào nút bên dưới để xác minh địa chỉ email của bạn.",
+  "Regards": "Trân trọng",
+  "Register": "Đăng ký",
+  "Remember Me": "Ghi Nhớ",
+  "Reset Password": "Đặt Lại Mật Khẩu",
+  "Reset Password Notification": "Thông Báo Đặt Lại Mật Khẩu",
+  "results": "kết quả",
+  "Send Password Reset Link": "Gửi Đường Dẫn Đặt Lại Mật Khẩu",
+  "Server Error": "Máy Chủ Gặp Sự Cố",
+  "Service Unavailable": "Dịch Vụ Không Khả Dụng",
+  "Showing": "Đang hiển thị",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "感谢您的注册! 在开始之前,您需要通过点击我们刚刚发送至您邮箱中的链接来验证您的电子邮件! 如果您没有收到电子邮件,我们很乐意再发送一封给您。",
+  "The :attribute must contain at least one letter.": "Trường :attribute phải chứa ít nhất một chữ cái.",
+  "The :attribute must contain at least one number.": "Trường :attribute phải chứa ít nhất một số.",
+  "The :attribute must contain at least one symbol.": "Trường :attribute must phải chứa ít nhất một ký hiệu.",
+  "The :attribute must contain at least one uppercase and one lowercase letter.": "Trường :attribute phải chứa ít nhất một chữ hoa và một chữ thường.",
+  "The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":attribute đã cho đã xuất hiện trong một vụ rò rỉ dữ liệu. Vui lòng chọn :attribute khác.",
+  "This password reset link will expire in :count minutes.": "Đường dẫn lấy lại mật khẩu sẽ hết hạn trong :count phút.",
+  "to": "tới",
+  "Toggle navigation": "Chuyển hướng điều hướng",
+  "Too Many Requests": "Quá Nhiều Yêu Cầu",
+  "Unauthorized": "Không Được Phép",
+  "Verify Email Address": "Xác Minh Địa Chỉ Email",
+  "Verify Your Email Address": "Xác Minh Địa Chỉ Email Của Bạn",
+  "Whoops!": "Rất tiếc!",
+  "You are receiving this email because we received a password reset request for your account.": "Bạn nhận được email này vì chúng tôi đã nhận được yêu cầu đặt lại mật khẩu cho tài khoản của bạn.",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": "超过:num小时未回复工单,系统已自动关闭了您的工单",
+  "You must have a valid subscription to view the content in this area!": "您必须拥有有效套餐才可以查看该区域的内容!",
+  "Daily Data Usage Report": "线路每日流量报告",
+  "Invoice Detail": "订单明细",
+  "Payment for #:sn has been received! Total amount: :amount.": "您成功支付了订单#:sn, 总金额为 :amount.",
+  "Payment Received": "账单付款成功",
+  "[Service Timer] Service Expiration": "[定时任务]服务到期",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[自动任务]封禁订阅: 订阅24小时内请求异常",
+  "[Auto Task] Blocked service: Run out of traffic": "[自动任务]封禁服务: 流量耗尽",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[自动任务]封禁服务: 1小时内流量异常",
+  "[Daily Task] Account Expiration: Stop Service": "[每日任务]账号过期: 停止服务",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[每日任务]账号过期: 禁止登录, 清空账户",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[每日任务]重置账号流量, 下次重置日: :date",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」完成, 耗时 :time 秒----",
+  "Subscription link receive abnormal access and banned by the system": "订阅链接访问异常,系统自动封禁",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "订阅功能被禁用,请联系管理员恢复"
 }

+ 11 - 11
resources/lang/vi/admin.php

@@ -610,7 +610,7 @@ return [
         'wechat_qrcode'                 => '微 信二维码',
         'wechat_secret'                 => '微信企业应用密钥',
         'wechat_token'                  => '微信企业应用TOKEN',
-        'hint'           => [
+        'hint'                          => [
             'account_expire_notification'   => '通知用户账号即将到期',
             'active_times'                  => '24小时内可以通过邮件激活账号次数',
             'admin_invite_days'             => '管理员生成邀请码的有效期',
@@ -687,7 +687,7 @@ return [
             'ticket_closed_notification'    => '工单关闭通知用户',
             'ticket_created_notification'   => '新工单通知管理/用户,取决于谁创建了新工单',
             'ticket_replied_notification'   => '工单回复通知对方',
-            'traffic_ban_time'              => '触发流量异常导致用户被封禁的时长,到期后自动解封',
+            'traffic_ban_time'              => '任何异常导致用户/订阅被封禁的时长, 到期后自动解封',
             'traffic_ban_value'             => '1小时内超过该值,则触发自动封号',
             'traffic_limit_time'            => '间隔多久才可以再次签到',
             'traffic_warning_percent'       => '【流量耗尽通知】开始阈值,每日通知用户',
@@ -708,7 +708,7 @@ return [
             'wechat_secret'                 => '应用的Secret(可能需要下载企业微信才能查看)',
             'wechat_token'                  => '应用管理->应用->设置API接收->TOKEN,URL设置::url',
         ],
-        'placeholder'    => [
+        'placeholder'                   => [
             'default_url'           => '默认为 :url',
             'server_chan_key'       => '填入ServerChan的SCKEY->再点击更新',
             'pushDeer_key'          => '填入PushDeer的Push Key -> 再点击更新',
@@ -724,7 +724,7 @@ return [
             'tg_chat_token'         => '请到Telegram申请',
             'codepay_url'           => 'https://codepay.fateqq.com/creat_order/?',
         ],
-        'payment'        => [
+        'payment'                       => [
             'attribute' => '支付设置',
             'channel'   => [
                 'alipay'    => '支付宝F2F',
@@ -747,7 +747,7 @@ return [
                 'manual'    => '设置后会自动开启对应显示',
             ],
         ],
-        'notification'   => [
+        'notification'                  => [
             'channel'   => [
                 'telegram'   => 'TG电报',
                 'wechat'     => '微信企业',
@@ -763,33 +763,33 @@ return [
             ],
             'send_test' => '发送测试消息',
         ],
-        'forbid'         => [
+        'forbid'                        => [
             'mainland' => '阻拦大陆',
             'china'    => '阻拦中国',
             'oversea'  => '阻拦海外',
         ],
-        'username'       => [
+        'username'                      => [
             'email'  => '电子邮箱',
             'mobile' => '手机号码',
             'any'    => '任意用户名',
         ],
-        'active_account' => [
+        'active_account'                => [
             'before' => '注册前激活',
             'after'  => '注册后激活',
         ],
-        'ddns'           => [
+        'ddns'                          => [
             'namesilo'   => 'Namesilo',
             'aliyun'     => '阿里云(国际&国内)',
             'dnspod'     => 'DNSPod',
             'cloudflare' => 'CloudFlare',
         ],
-        'captcha'        => [
+        'captcha'                       => [
             'standard'  => '普通验证码',
             'geetest'   => '极 验',
             'recaptcha' => 'Google reCaptcha',
             'hcaptcha'  => 'hCaptcha',
         ],
-        'referral'       => [
+        'referral'                      => [
             'once' => '首购返利',
             'loop' => '循环返利',
         ],

+ 2 - 2
resources/lang/vi/common.php

@@ -86,7 +86,7 @@ return [
         'crypto' => '虚拟货币',
         'manual' => '人工支付',
         'status' => [
-            'wait'    => '待支付',
+            'wait' => '待支付',
         ],
     ],
     'order'           => [
@@ -124,7 +124,7 @@ return [
         'pending'           => '待处理',
         'unknown'           => '未 知',
         'available'         => '生效中',
-        'reject'            => '驳 回',
+        'reject'            => '否 決',
         'rejected'          => '已驳回',
         'review'            => '待审核',
         'reviewed'          => '审核通过待打款',

+ 3 - 3
resources/lang/vi/model.php

@@ -217,9 +217,9 @@ return [
         'log_time' => '记录时间',
     ],
     'user_data_modify' => [
-        'before'      => '变动前流量',
-        'after'       => '变动后流量',
-        'created_at'  => '发生时间',
+        'before'     => '变动前流量',
+        'after'      => '变动后流量',
+        'created_at' => '发生时间',
     ],
     'user_credit'      => [
         'before'     => '操作前余额',

+ 0 - 1
resources/lang/vi/user.php

@@ -171,7 +171,6 @@ return [
         'rate'     => ':ratio倍流量消耗',
     ],
     'subscribe'           => [
-        'baned'            => '您的订阅功能被禁用,请联系管理员恢复',
         'link'             => '订阅链接',
         'tips'             => '警告:该订阅链接仅限个人使用,请勿传播该链接,否则会导致您的账号流量使用情况异常触发自动封号机制。',
         'exchange_warning' => '更换订阅地址将导致:\n1.旧地址立即失效\n2.连接密码被更改',

+ 66 - 52
resources/lang/zh_CN.json

@@ -1,54 +1,68 @@
 {
-    "A fresh verification link has been sent to your email address.": "一个新的验证链接已发送到您的电子邮件地址。",
-    "All rights reserved.": "版权所有。",
-    "Before proceeding, please check your email for a verification link.": "在继续之前,请检查您的电子邮件以获取验证链接。",
-    "click here to request another": "单击此处请求另一个",
-    "Confirm Password": "确认密码",
-    "E-Mail Address": "电子邮件地址",
-    "Forbidden": "访问被拒绝",
-    "Forgot Your Password?": "忘记密码?",
-    "Go Home": "回首页",
-    "Go to page :page": "前往第 :page 页",
-    "Hello!": "您好!",
-    "If you did not create an account, no further action is required.": "如果您未注册帐号,请忽略此邮件。",
-    "If you did not receive the email": "如果您没有收到电子邮件",
-    "If you did not request a password reset, no further action is required.": "如果您未申请重设密码,请忽略此邮件。",
-    "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "如果您单击「:actionText」按钮时遇到问题,请复制下方链接到浏览器中访问:",
-    "Login": "登录",
-    "Logout": "登出",
-    "Name": "姓名",
-    "Not Found": "页面不存在",
-    "of": "于",
-    "Oh no": "不好了",
-    "Page Expired": "页面会话已超时",
-    "Pagination Navigation": "分页导航",
-    "Password": "密码",
-    "Please click the button below to verify your email address.": "请点击下面按钮验证您的 E-mail:",
-    "Regards": "致敬",
-    "Register": "注册",
-    "Remember Me": "记住我",
-    "Reset Password": "重置密码",
-    "Reset Password Notification": "重置密码通知",
-    "results": "结果",
-    "Send Password Reset Link": "发送重设密码链接",
-    "Server Error": "服务器错误",
-    "Service Unavailable": "服务不可用",
-    "Showing": "显示中",
-    "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "感谢您的注册! 在开始之前,您需要通过点击我们刚刚发送至您邮箱中的链接来验证您的电子邮件! 如果您没有收到电子邮件,我们很乐意再发送一封给您。",
-    "The :attribute must contain at least one letter.": ":attribute 至少包含一个字母。",
-    "The :attribute must contain at least one number.": ":attribute 至少包含一个数字。",
-    "The :attribute must contain at least one symbol.": ":attribute 至少包含一个符号。",
-    "The :attribute must contain at least one uppercase and one lowercase letter.": ":attribute 至少包含一个大写字母和一个小写字母。",
-    "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "给定的 :attribute 出现在数据泄漏中。请选择不同的 :attribute。",
-    "This password reset link will expire in :count minutes.": "这个重设密码链接将会在 :count 分钟后失效。",
-    "to": "至",
-    "Toggle navigation": "切换导航",
-    "Too Many Requests": "请求次数过多。",
-    "Unauthorized": "未授权",
-    "Verify Email Address": "验证 E-mail",
-    "Verify Your Email Address": "验证您的邮件地址",
-    "Whoops!": "哎呀!",
-    "You are receiving this email because we received a password reset request for your account.": "您收到此电子邮件是因为我们收到了您帐户的密码重设请求。",
-    "You have not responded this ticket in :num hours, System has closed your ticket.": "超过:num小时未回复工单,系统已自动关闭了您的工单",
-    "You must have a valid subscription to view the content in this area!": "您必须拥有有效套餐才可以查看该区域的内容!"
+  "A fresh verification link has been sent to your email address.": "一个新的验证链接已发送到您的电子邮件地址。",
+  "All rights reserved.": "版权所有。",
+  "Before proceeding, please check your email for a verification link.": "在继续之前,请检查您的电子邮件以获取验证链接。",
+  "click here to request another": "单击此处请求另一个",
+  "Confirm Password": "确认密码",
+  "E-Mail Address": "电子邮件地址",
+  "Forbidden": "访问被拒绝",
+  "Forgot Your Password?": "忘记密码?",
+  "Go Home": "回首页",
+  "Go to page :page": "前往第 :page 页",
+  "Hello!": "您好!",
+  "If you did not create an account, no further action is required.": "如果您未注册帐号,请忽略此邮件。",
+  "If you did not receive the email": "如果您没有收到电子邮件",
+  "If you did not request a password reset, no further action is required.": "如果您未申请重设密码,请忽略此邮件。",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "如果您单击「:actionText」按钮时遇到问题,请复制下方链接到浏览器中访问:",
+  "Login": "登录",
+  "Logout": "登出",
+  "Name": "姓名",
+  "Not Found": "页面不存在",
+  "of": "于",
+  "Oh no": "不好了",
+  "Page Expired": "页面会话已超时",
+  "Pagination Navigation": "分页导航",
+  "Password": "密码",
+  "Please click the button below to verify your email address.": "请点击下面按钮验证您的 E-mail:",
+  "Regards": "致敬",
+  "Register": "注册",
+  "Remember Me": "记住我",
+  "Reset Password": "重置密码",
+  "Reset Password Notification": "重置密码通知",
+  "results": "结果",
+  "Send Password Reset Link": "发送重设密码链接",
+  "Server Error": "服务器错误",
+  "Service Unavailable": "服务不可用",
+  "Showing": "显示中",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "感谢您的注册! 在开始之前,您需要通过点击我们刚刚发送至您邮箱中的链接来验证您的电子邮件! 如果您没有收到电子邮件,我们很乐意再发送一封给您。",
+  "The :attribute must contain at least one letter.": ":attribute 至少包含一个字母。",
+  "The :attribute must contain at least one number.": ":attribute 至少包含一个数字。",
+  "The :attribute must contain at least one symbol.": ":attribute 至少包含一个符号。",
+  "The :attribute must contain at least one uppercase and one lowercase letter.": ":attribute 至少包含一个大写字母和一个小写字母。",
+  "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "给定的 :attribute 出现在数据泄漏中。请选择不同的 :attribute。",
+  "This password reset link will expire in :count minutes.": "这个重设密码链接将会在 :count 分钟后失效。",
+  "to": "至",
+  "Toggle navigation": "切换导航",
+  "Too Many Requests": "请求次数过多。",
+  "Unauthorized": "未授权",
+  "Verify Email Address": "验证 E-mail",
+  "Verify Your Email Address": "验证您的邮件地址",
+  "Whoops!": "哎呀!",
+  "You are receiving this email because we received a password reset request for your account.": "您收到此电子邮件是因为我们收到了您帐户的密码重设请求。",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": "超过:num小时未回复工单,系统已自动关闭了您的工单",
+  "You must have a valid subscription to view the content in this area!": "您必须拥有有效套餐才可以查看该区域的内容!",
+  "Daily Data Usage Report": "线路每日流量报告",
+  "Invoice Detail": "订单明细",
+  "Payment for #:sn has been received! Total amount: :amount.": "您成功支付了订单#:sn, 总金额为 :amount.",
+  "Payment Received": "账单付款成功",
+  "[Service Timer] Service Expiration": "[定时任务]服务到期",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[自动任务]封禁订阅: 订阅24小时内请求异常",
+  "[Auto Task] Blocked service: Run out of traffic": "[自动任务]封禁服务: 流量耗尽",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[自动任务]封禁服务: 1小时内流量异常",
+  "[Daily Task] Account Expiration: Stop Service": "[每日任务]账号过期: 停止服务",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[每日任务]账号过期: 禁止登录, 清空账户",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[每日任务]重置账号流量, 下次重置日: :date",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」完成, 耗时 :time 秒----",
+  "Subscription link receive abnormal access and banned by the system": "订阅链接访问异常,系统自动封禁",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "订阅功能被禁用,请联系管理员恢复"
 }

+ 11 - 11
resources/lang/zh_CN/admin.php

@@ -610,7 +610,7 @@ return [
         'wechat_qrcode'                 => '微 信二维码',
         'wechat_secret'                 => '微信企业应用密钥',
         'wechat_token'                  => '微信企业应用TOKEN',
-        'hint'           => [
+        'hint'                          => [
             'account_expire_notification'   => '通知用户账号即将到期',
             'active_times'                  => '24小时内可以通过邮件激活账号次数',
             'admin_invite_days'             => '管理员生成邀请码的有效期',
@@ -687,7 +687,7 @@ return [
             'ticket_closed_notification'    => '工单关闭通知用户',
             'ticket_created_notification'   => '新工单通知管理/用户,取决于谁创建了新工单',
             'ticket_replied_notification'   => '工单回复通知对方',
-            'traffic_ban_time'              => '触发流量异常导致用户被封禁的时长,到期后自动解封',
+            'traffic_ban_time'              => '任何异常导致用户/订阅被封禁的时长, 到期后自动解封',
             'traffic_ban_value'             => '1小时内超过该值,则触发自动封号',
             'traffic_limit_time'            => '间隔多久才可以再次签到',
             'traffic_warning_percent'       => '【流量耗尽通知】开始阈值,每日通知用户',
@@ -708,7 +708,7 @@ return [
             'wechat_secret'                 => '应用的Secret(可能需要下载企业微信才能查看)',
             'wechat_token'                  => '应用管理->应用->设置API接收->TOKEN,URL设置::url',
         ],
-        'placeholder'    => [
+        'placeholder'                   => [
             'default_url'           => '默认为 :url',
             'server_chan_key'       => '填入ServerChan的SCKEY->再点击更新',
             'pushDeer_key'          => '填入PushDeer的Push Key -> 再点击更新',
@@ -724,7 +724,7 @@ return [
             'tg_chat_token'         => '请到Telegram申请',
             'codepay_url'           => 'https://codepay.fateqq.com/creat_order/?',
         ],
-        'payment'        => [
+        'payment'                       => [
             'attribute' => '支付设置',
             'channel'   => [
                 'alipay'    => '支付宝F2F',
@@ -747,7 +747,7 @@ return [
                 'manual'    => '设置后会自动开启对应显示',
             ],
         ],
-        'notification'   => [
+        'notification'                  => [
             'channel'   => [
                 'telegram'   => 'TG电报',
                 'wechat'     => '微信企业',
@@ -763,33 +763,33 @@ return [
             ],
             'send_test' => '发送测试消息',
         ],
-        'forbid'         => [
+        'forbid'                        => [
             'mainland' => '阻拦大陆',
             'china'    => '阻拦中国',
             'oversea'  => '阻拦海外',
         ],
-        'username'       => [
+        'username'                      => [
             'email'  => '电子邮箱',
             'mobile' => '手机号码',
             'any'    => '任意用户名',
         ],
-        'active_account' => [
+        'active_account'                => [
             'before' => '注册前激活',
             'after'  => '注册后激活',
         ],
-        'ddns'           => [
+        'ddns'                          => [
             'namesilo'   => 'Namesilo',
             'aliyun'     => '阿里云(国际&国内)',
             'dnspod'     => 'DNSPod',
             'cloudflare' => 'CloudFlare',
         ],
-        'captcha'        => [
+        'captcha'                       => [
             'standard'  => '普通验证码',
             'geetest'   => '极 验',
             'recaptcha' => 'Google reCaptcha',
             'hcaptcha'  => 'hCaptcha',
         ],
-        'referral'       => [
+        'referral'                      => [
             'once' => '首购返利',
             'loop' => '循环返利',
         ],

+ 1 - 1
resources/lang/zh_CN/common.php

@@ -86,7 +86,7 @@ return [
         'crypto' => '虚拟货币',
         'manual' => '人工支付',
         'status' => [
-            'wait'    => '待支付',
+            'wait' => '待支付',
         ],
     ],
     'order'           => [

+ 3 - 3
resources/lang/zh_CN/model.php

@@ -217,9 +217,9 @@ return [
         'log_time' => '记录时间',
     ],
     'user_data_modify' => [
-        'before'      => '变动前流量',
-        'after'       => '变动后流量',
-        'created_at'  => '发生时间',
+        'before'     => '变动前流量',
+        'after'      => '变动后流量',
+        'created_at' => '发生时间',
     ],
     'user_credit'      => [
         'before'     => '操作前余额',

+ 0 - 1
resources/lang/zh_CN/user.php

@@ -171,7 +171,6 @@ return [
         'rate'     => ':ratio倍流量消耗',
     ],
     'subscribe'           => [
-        'baned'            => '您的订阅功能被禁用,请联系管理员恢复',
         'link'             => '订阅链接',
         'tips'             => '警告:该订阅链接仅限个人使用,请勿传播该链接,否则会导致您的账号流量使用情况异常触发自动封号机制。',
         'exchange_warning' => '更换订阅地址将导致:\n1.旧地址立即失效\n2.连接密码被更改',

+ 3 - 3
resources/views/admin/logs/order.blade.php

@@ -127,19 +127,19 @@
                                         @if ($order->status !== -1)
                                             <a class="dropdown-item" href="javascript:changeStatus('{{$order->id}}', -1)" role="menuitem">
                                                 <i class="icon wb-close" aria-hidden="true"></i>
-                                                {{ trans('admin.set_to', ['attribute' => trans('common.status.expire')]) }}
+                                                {{ trans('admin.set_to', ['attribute' => $order->statusTags(-1, 0, false)]) }}
                                             </a>
                                         @endif
                                         @if ($order->status !== 2)
                                             <a class="dropdown-item" href="javascript:changeStatus('{{$order->id}}', 2)" role="menuitem">
                                                 <i class="icon wb-check" aria-hidden="true"></i>
-                                                {{ trans('admin.set_to', ['attribute' => trans('common.order.status.complete')]) }}
+                                                {{ trans('admin.set_to', ['attribute' => $order->statusTags(2, 0, false)]) }}
                                             </a>
                                         @endif
                                         @if ($order->status !== 3)
                                             <a class="dropdown-item" href="javascript:changeStatus('{{$order->id}}', 3)" role="menuitem">
                                                 <i class="icon wb-check-circle" aria-hidden="true"></i>
-                                                {{ trans('admin.set_to', ['attribute' => trans('common.order.status.prepaid')]) }}
+                                                {{ trans('admin.set_to', ['attribute' => $order->statusTags(3, 0, false)]) }}
                                             </a>
                                         @endif
                                     </div>

+ 1 - 1
resources/views/admin/subscribe/index.blade.php

@@ -73,7 +73,7 @@
                             </td>
                             <td> {{$subscribe->updated_at}} </td>
                             <td> {{$subscribe->ban_time ? date('Y-m-d H:i', $subscribe->ban_time): ''}} </td>
-                            <td> {{$subscribe->ban_desc}} </td>
+                            <td> {{ __($subscribe->ban_desc) }} </td>
                             <td>
                                 @can('admin.subscribe.set')
                                     <button class="btn btn-sm @if($subscribe->status === 0) btn-outline-success @else btn-outline-danger @endif"

+ 1 - 1
resources/views/admin/subscribe/log.blade.php

@@ -62,7 +62,7 @@
                                 $subscribe->ban_time ) }}</span>
                             </li>
                             <li class="list-group-item bg-blue-grey-100">
-                                <i class="icon wb-lock" aria-hidden="true"></i> {{ trans('model.subscribe.ban_desc') }}: <span class="float-right">{{ $subscribe->ban_desc }}</span>
+                                <i class="icon wb-lock" aria-hidden="true"></i> {{ trans('model.subscribe.ban_desc') }}: <span class="float-right">{{ __($subscribe->ban_desc) }}</span>
                             </li>
                         @endif
                         @can('admin.subscribe.set')

+ 1 - 1
resources/views/user/index.blade.php

@@ -185,7 +185,7 @@
                                             {{trans('common.copy.attribute')}}</button>
                                     </div>
                                 @else
-                                    <x-alert type="danger" :message="trans('user.subscribe.baned')"/>
+                                    <x-alert type="danger" :message="__($subMsg)"/>
                                 @endif
                             </div>
                         </div>

+ 1 - 1
resources/views/user/knowledge.blade.php

@@ -96,7 +96,7 @@
                                                                     </div>
                                                                 </div>
                                                             @else
-                                                                <x-alert type="danger" :message="trans('user.subscribe.baned')"/>
+                                                                <x-alert type="danger" :message="__($subMsg)"/>
                                                             @endif
                                                         </div>
                                                     </div>