alroyso 1 month ago
parent
commit
2799822f5e

+ 18 - 2
.idea/workspace.xml

@@ -5,10 +5,17 @@
   </component>
   <component name="ChangeListManager">
     <list default="true" id="09451f28-815a-407f-8951-727d305b50a4" name="Changes" comment="Changes">
+      <change afterPath="$PROJECT_DIR$/public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/app/Console/Commands/AutoJob.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Console/Commands/AutoJob.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/app/Console/Commands/DailyJob.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Console/Commands/DailyJob.php" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/app/Http/Controllers/Admin/LogsController.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/Admin/LogsController.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/app/Http/Controllers/Api/Client/V1Controller.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/Api/Client/V1Controller.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/app/Http/Controllers/Api/Client/V2Controller.php" beforeDir="false" afterPath="$PROJECT_DIR$/app/Http/Controllers/Api/Client/V2Controller.php" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/resources/views/admin/logs/traffic.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/admin/logs/traffic.blade.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/resources/views/admin/logs/userBanHistory.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/admin/logs/userBanHistory.blade.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/resources/views/admin/user/index.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/admin/user/index.blade.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/resources/views/admin/user/info.blade.php" beforeDir="false" afterPath="$PROJECT_DIR$/resources/views/admin/user/info.blade.php" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -386,7 +393,8 @@
       <workItem from="1745205587369" duration="5133000" />
       <workItem from="1746161383498" duration="16509000" />
       <workItem from="1746179823316" duration="256000" />
-      <workItem from="1746180473097" duration="1860000" />
+      <workItem from="1746180473097" duration="1874000" />
+      <workItem from="1746240782987" duration="12456000" />
     </task>
     <task id="LOCAL-00001" summary="Changes">
       <option name="closed" value="true" />
@@ -708,7 +716,15 @@
       <option name="project" value="LOCAL" />
       <updated>1746180074310</updated>
     </task>
-    <option name="localTasksCounter" value="41" />
+    <task id="LOCAL-00041" summary="Changes">
+      <option name="closed" value="true" />
+      <created>1746182343209</created>
+      <option name="number" value="00041" />
+      <option name="presentableId" value="LOCAL-00041" />
+      <option name="project" value="LOCAL" />
+      <updated>1746182343209</updated>
+    </task>
+    <option name="localTasksCounter" value="42" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">

+ 58 - 36
app/Console/Commands/AutoJob.php

@@ -47,69 +47,91 @@ class AutoJob extends Command
 
     private function blockUsers(): void
     {
-        // 禁用流量超限用户
+        // 封顶禁用:已使用流量 >= 配额
         User::activeUser()
             ->whereRaw('u + d >= transfer_enable')
             ->chunk(100, function ($users) {
                 foreach ($users as $user) {
-                    $user->update(['enable' => 0]);
-                    Helpers::addUserBanLog($user->id, 0, '【封禁代理】-流量已用完');
-                    \Log::info('用户流量超限被封禁', [
-                        'user_id' => $user->id,
-                        'email' => $user->email,
-                        'used_traffic' => $user->u + $user->d,
-                        'transfer_enable' => $user->transfer_enable,
-                        'ban_time' => now()
-                    ]);
+                    try {
+                        $user->update(['enable' => 0]);
+                        Helpers::addUserBanLog($user->id, 0, '【封禁代理】-流量已用完');
+                        \Log::info('用户流量超限被封禁', [
+                            'user_id' => $user->id,
+                            'email' => $user->email,
+                            'used_traffic' => $user->u + $user->d,
+                            'transfer_enable' => $user->transfer_enable,
+                            'ban_time' => now()
+                        ]);
+                    } catch (\Throwable $e) {
+                        \Log::error("封禁超限用户失败 [ID: {$user->id}]", [
+                            'error' => $e->getMessage()
+                        ]);
+                    }
                 }
             });
 
-        // 封禁最近1小时内流量异常账号(直接用UserDataFlowLog统计)
+        // 加权封禁:1小时内加权流量超限
         if (sysConfig('is_traffic_ban')) {
             $trafficBanTime = sysConfig('traffic_ban_time');
-            $trafficBanValue = sysConfig('traffic_ban_value') * GB;
+            $baseTrafficBanValue = sysConfig('traffic_ban_value') * GB;
             $oneHourAgo = time() - 3600;
 
-            // 查询最近1小时内流量超限的用户ID
-            $abnormalUserIds = UserDataFlowLog::where('log_time', '>=', $oneHourAgo)
-                ->selectRaw('user_id, SUM(u + d) as total_traffic')
-                ->groupBy('user_id')
-                ->having('total_traffic', '>', $trafficBanValue)
-                ->pluck('user_id')
-                ->toArray();
+            // 提前加载所有节点到内存,避免 N+1 查询
+            $nodes = Node::all()->keyBy('id');
+
+            // 加权流量累加器
+            $userWeightedTotals = [];
+
+            UserDataFlowLog::where('log_time', '>=', $oneHourAgo)
+                ->chunk(1000, function ($records) use (&$userWeightedTotals, $nodes) {
+                    foreach ($records as $record) {
+                        $user_id = $record->user_id;
+                        $node = $nodes->get($record->node_id);
+                        $rate = $node ? ($node->traffic_rate > 0 ? $node->traffic_rate : 1) : 1;
+
+                        $weighted = ($record->u + $record->d) / $rate;
+
+                        if (!isset($userWeightedTotals[$user_id])) {
+                            $userWeightedTotals[$user_id] = 0;
+                        }
+                        $userWeightedTotals[$user_id] += $weighted;
+                    }
+                });
+
+            $abnormalUserIds = [];
+            foreach ($userWeightedTotals as $user_id => $weightedTotal) {
+                if ($weightedTotal > $baseTrafficBanValue) {
+                    $user = User::find($user_id);
+                    if (!$user) {
+                        \Log::warning("⚠️ 找不到用户 [ID: $user_id],跳过封禁");
+                        continue;
+                    }
+                    \Log::warning("⚠️ 用户 [ID: $user_id, 邮箱: {$user->email}] 1小时流量为 ".flowAutoShow($weightedTotal).",超出阈值 ".flowAutoShow($baseTrafficBanValue));
+                    $abnormalUserIds[] = $user_id;
+                }
+            }
 
             if (!empty($abnormalUserIds)) {
                 \Log::info('发现流量异常用户', [
                     'count' => count($abnormalUserIds),
-                    'user_ids' => $abnormalUserIds
+                    'user_ids' => $abnormalUserIds,
                 ]);
 
                 User::activeUser()
                     ->whereIn('id', $abnormalUserIds)
                     ->whereBanTime(null)
-                    ->chunk(100, function ($users) use ($trafficBanTime, $trafficBanValue, $oneHourAgo) {
+                    ->chunk(100, function ($users) use ($trafficBanTime) {
                         foreach ($users as $user) {
                             try {
-                                // 重新统计该用户最近1小时的总流量
-                                $total = UserDataFlowLog::where('user_id', $user->id)
-                                    ->where('log_time', '>=', $oneHourAgo)
-                                    ->sum(\DB::raw('u + d'));
-                                $banValueStr = flowAutoShow($trafficBanValue);
-                                $usedStr = flowAutoShow($total);
-
-                                \Log::warning('⚠️ 用户流量异常封禁 | ID: '.$user->id.' | 邮箱: '.$user->email.' | 1小时内已用流量: '.$usedStr.',阈值: '.$banValueStr.'(已超限)');
-
                                 $user->update([
                                     'enable' => 0,
-                                    'ban_time' => strtotime("+$trafficBanTime minutes")
+                                    'ban_time' => strtotime("+$trafficBanTime minutes"),
                                 ]);
                                 $user->refresh();
-
                                 Helpers::addUserBanLog($user->id, $trafficBanTime, '【临时封禁代理】-1小时内流量异常');
-                            } catch (\Exception $e) {
-                                \Log::error('封禁流量异常用户失败', [
-                                    'user_id' => $user->id,
-                                    'error' => $e->getMessage()
+                            } catch (\Throwable $e) {
+                                \Log::error("封禁加权流量异常用户失败 [ID: {$user->id}]", [
+                                    'error' => $e->getMessage(),
                                 ]);
                             }
                         }

+ 73 - 68
app/Console/Commands/DailyJob.php

@@ -39,45 +39,48 @@ class DailyJob extends Command
     private function expireUser(): void
     {
         // 过期用户处理
-        $userList = User::activeUser()->where('expired_at', '<', date('Y-m-d H:i:s'))->get();
         $isBanStatus = sysConfig('is_ban_status');
-        foreach ($userList 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,
-                ]);
-
-                Helpers::addUserBanLog($user->id, 0, '【禁止登录,清空账户】-账号已过期');
-
-                // 废除其名下邀请码
-                $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,
-                ]);
-
-                Helpers::addUserBanLog($user->id, 0, '【封禁代理,清空账户】-账号已过期');
-
-                // 写入用户流量变动记录
-                Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, 0, '[定时任务]账号已过期(封禁代理,清空账户)');
-            }
-        }
+        User::activeUser()
+            ->where('expired_at', '<', date('Y-m-d H:i:s'))
+            ->chunk(100, function ($userList) use ($isBanStatus) {
+                foreach ($userList 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,
+                        ]);
+
+                        Helpers::addUserBanLog($user->id, 0, '【禁止登录,清空账户】-账号已过期');
+
+                        // 废除其名下邀请码
+                        $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,
+                        ]);
+
+                        Helpers::addUserBanLog($user->id, 0, '【封禁代理,清空账户】-账号已过期');
+
+                        // 写入用户流量变动记录
+                        Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, 0, '[定时任务]账号已过期(封禁代理,清空账户)');
+                    }
+                }
+            });
     }
 
     // 关闭超过72小时未处理的工单
@@ -93,37 +96,39 @@ class DailyJob extends Command
     // 重置用户流量
     private function resetUserTraffic(): void
     {
-        $userList = User::where('status', '<>', -1)
+        User::where('status', '<>', -1)
             ->where('expired_at', '>', date('Y-m-d H:i:s'))
             ->where('reset_time', '<=', date('Y-m-d H:i:s'))
-            ->get();
-        foreach ($userList as $user) {
-            // 跳过 没有重置日期的账号
-            if (! $user->reset_time) {
-                continue;
-            }
-
-            // 取出用户正在使用的套餐
-            $order = Order::userActivePlan($user->id)->first();
-
-            // 无订单用户跳过
-            if (! $order) {
-                continue;
-            }
-
-            // 过期生效中的加油包
-            Order::userActivePackage($user->id)->update(['is_expire' => 1]);
-
-            $oldData = $user->transfer_enable;
-            // 重置流量与重置日期
-            $ret = $user->update((new OrderService($order))->resetTimeAndData($user->expired_at));
-            if ($ret) {
-                // 可用流量变动日志
-                Helpers::addUserTrafficModifyLog($order->user_id, $order->id, $oldData, $user->transfer_enable, '【流量重置】重置可用流量');
-                Log::info('用户[ID:'.$user->id.'  昵称: '.$user->username.'  邮箱: '.$user->email.'] 流量重置为 '.flowAutoShow($user->transfer_enable).'. 重置日期为 '.($user->reset_time ?: '【无】'));
-            } else {
-                Log::warning('用户[ID:'.$user->id.'  昵称: '.$user->username.'  邮箱: '.$user->email.'] 流量重置失败');
-            }
-        }
+            ->chunk(100, function ($userList) {
+                foreach ($userList as $user) {
+                    // 跳过没有重置日期的账号
+                    if (!$user->reset_time) {
+                        continue;
+                    }
+
+                    // 取出用户正在使用的套餐
+                    $order = Order::userActivePlan($user->id)->first();
+
+                    // 无订单用户跳过
+                    if (!$order) {
+                        continue;
+                    }
+
+                    // 过期生效中的加油包
+                    Order::userActivePackage($user->id)->update(['is_expire' => 1]);
+
+                    $oldData = $user->transfer_enable;
+                    // 重置流量与重置日期
+                    $ret = $user->update((new OrderService($order))->resetTimeAndData($user->expired_at));
+                    if ($ret) {
+                        // 可用流量变动日志
+                        Helpers::addUserTrafficModifyLog($order->user_id, $order->id, $oldData, $user->transfer_enable, '【流量重置】重置可用流量');
+                        // 单独输出每个用户的重置日志
+                        Log::info('【流量重置】用户[ID:'.$user->id.'  昵称: '.$user->username.'  邮箱: '.$user->email.'] 流量重置为 '.flowAutoShow($user->transfer_enable).'. 重置日期为 '.($user->reset_time ?: '【无】'));
+                    } else {
+                        Log::warning('【流量重置失败】用户[ID:'.$user->id.'  昵称: '.$user->username.'  邮箱: '.$user->email.']');
+                    }
+                }
+            });
     }
 }

+ 60 - 2
app/Http/Controllers/Admin/LogsController.php

@@ -112,6 +112,7 @@ class LogsController extends Controller
         $startTime = $request->input('startTime');
         $endTime = $request->input('endTime');
         $lastHours = $request->input('last_hours');
+        $isAbnormal = $request->input('is_abnormal'); // 新增异常用户筛选
 
         $query = UserDataFlowLog::with(['user', 'node']);
 
@@ -135,6 +136,7 @@ class LogsController extends Controller
             $query->whereNodeId($nodeId);
         }
 
+        // 处理时间范围
         if ($lastHours) {
             $query->where('log_time', '>=', time() - 3600 * intval($lastHours));
         } else {
@@ -146,17 +148,60 @@ class LogsController extends Controller
             }
         }
 
+        $baseTrafficBanValue = sysConfig('traffic_ban_value') * GB;
+
+        if ($isAbnormal) {
+            // 只查最近N小时,避免全表扫描
+            $hours = $lastHours ?: 1;
+            $since = time() - 3600 * intval($hours);
+
+            // 先查出最近N小时内所有流量日志
+            $logs = UserDataFlowLog::with('node')
+                ->where('log_time', '>=', $since)
+                ->get();
+
+            // 统计每个用户的"加权流量"
+            $userWeighted = [];
+            foreach ($logs as $log) {
+                $rate = ($log->node && $log->node->traffic_rate) ? $log->node->traffic_rate : 1;
+                if ($rate <= 0) $rate = 1;
+                $weighted = ($log->u + $log->d) / $rate;
+                if (!isset($userWeighted[$log->user_id])) {
+                    $userWeighted[$log->user_id] = 0;
+                }
+                $userWeighted[$log->user_id] += $weighted;
+            }
+
+            // 找出超限的用户ID
+            $abnormalUserIds = [];
+            foreach ($userWeighted as $userId => $total) {
+                if ($total > $baseTrafficBanValue) {
+                    $abnormalUserIds[] = $userId;
+                }
+            }
+
+            // 只查异常用户
+            $query->whereIn('user_id', $abnormalUserIds);
+        }
+
         $dataFlowLogs = $query->latest('log_time')->paginate(20)->appends($request->except('page'));
+
         foreach ($dataFlowLogs as $log) {
+            $rate = ($log->node && $log->node->traffic_rate) ? $log->node->traffic_rate : 1;
+            if ($rate <= 0) $rate = 1;
+            $weighted = ($log->u + $log->d) / $rate;
+            $log->weighted_traffic = $weighted;
+            $log->is_abnormal = $weighted > $baseTrafficBanValue;
             $log->u = flowAutoShow($log->u);
             $log->d = flowAutoShow($log->d);
             $log->log_time = date('Y-m-d H:i:s', $log->log_time);
         }
 
         return view('admin.logs.traffic', [
-            'totalTraffic' => flowAutoShow($query->sum('u') + $query->sum('d')), // 已使用流量
+            'totalTraffic' => flowAutoShow($query->sum('u') + $query->sum('d')),
             'dataFlowLogs' => $dataFlowLogs,
             'nodes' => Node::whereStatus(1)->orderByDesc('sort')->latest()->get(),
+            'isAbnormal' => $isAbnormal // 传递筛选状态到视图
         ]);
     }
 
@@ -254,6 +299,7 @@ class LogsController extends Controller
     public function userBanLogList(Request $request)
     {
         $email = $request->input('email');
+        $isBanned = $request->input('is_banned'); // 新增封禁状态筛选
 
         $query = UserBanedLog::with('user:id,email,t')->latest();
 
@@ -263,7 +309,19 @@ class LogsController extends Controller
             });
         }
 
-        return view('admin.logs.userBanHistory', ['userBanLogs' => $query->paginate(15)->appends($request->except('page'))]);
+        // 添加封禁状态筛选
+        if (isset($isBanned)) {
+            if ($isBanned == 1) {
+                $query->where('time', '>', 0); // 有封禁时长的记录
+            } else {
+                $query->where('time', 0); // 无封禁时长的记录
+            }
+        }
+
+        return view('admin.logs.userBanHistory', [
+            'userBanLogs' => $query->paginate(15)->appends($request->except('page')),
+            'isBanned' => $isBanned // 传递当前筛选状态到视图
+        ]);
     }
 
     // 用户流量变动记录

+ 7 - 0
app/Http/Controllers/Api/Client/V1Controller.php

@@ -93,6 +93,13 @@ class V1Controller extends Controller
             return response()->json(['ret' => 0, 'msg' => "暂停注册,升级服务器"]);
         }
 
+
+        // 再次判断用户是否已存在,防止并发或唯一索引未生效
+        $email = $request->input('email');
+        if (User::where('email', $email)->exists()) {
+            return response()->json(['ret' => 0, 'msg' => "该邮箱已注册"], 200);
+        }
+
         $transfer_enable = MB * ((int) sysConfig('default_traffic'));
 
         // 创建新用户

+ 8 - 2
app/Http/Controllers/Api/Client/V2Controller.php

@@ -124,11 +124,17 @@ class V2Controller extends Controller
             return response()->json(['ret' => 0, 'msg' => "暂停注册,升级服务器"]);
         }
 
-        $username = Helpers::GetRandStr(6);
-
         if ($validator->fails()) {
             return response()->json(['ret' => 0, 'msg' => implode("", $validator->errors()->all())],200);
         }
+
+        // 再次判断用户是否已存在,防止并发或唯一索引未生效
+        $email = $request->input('email');
+        if (User::where('email', $email)->exists()) {
+            return response()->json(['ret' => 0, 'msg' => "该邮箱已注册"], 200);
+        }
+
+        $username = Helpers::GetRandStr(6);
         $transfer_enable = MB * ((int) sysConfig('default_traffic'));
 
         // 创建新用户

+ 18 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js

@@ -0,0 +1,18 @@
+/*!
+ * Simplified Chinese translation for bootstrap-datepicker
+ * Yuan Cheung <advanimal@gmail.com>
+ */
+;(function($){
+	$.fn.datepicker.dates['zh-CN'] = {
+		days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
+		daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+		daysMin: ["日", "一", "二", "三", "四", "五", "六"],
+		months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+		monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+		today: "今天",
+		clear: "清除",
+		format: "yyyy-mm-dd",
+		titleFormat: "yyyy年mm月",
+		weekStart: 1
+	};
+}(jQuery)); 

+ 19 - 3
resources/views/admin/logs/traffic.blade.php

@@ -21,7 +21,7 @@
                         <input type="number" class="form-control" name="port" id="port" value="{{Request::input('port')}}" placeholder="用户端口"/>
                     </div>
                     <div class="form-group col-lg-3 col-sm-8">
-                        <select class="form-control" name="nodeId" id="nodeId" onChange="Search()">
+                        <select class="form-control" name="nodeId" id="nodeId">
                             <option value="" @if(Request::input('nodeId') == '') selected @endif hidden>选择节点</option>
                             @foreach($nodes as $node)
                                 <option value="{{$node->id}}" @if(Request::input('nodeId') == $node->id) selected @endif>
@@ -30,6 +30,12 @@
                             @endforeach
                         </select>
                     </div>
+                    <div class="form-group col-lg-2 col-sm-4">
+                        <select class="form-control" name="is_abnormal" id="is_abnormal">
+                            <option value="">全部用户</option>
+                            <option value="1" {{Request::input('is_abnormal') == '1' ? 'selected' : ''}}>异常用户</option>
+                        </select>
+                    </div>
                     <div class="form-group col-lg-6 col-sm-12">
                         <div class="input-group input-daterange" data-plugin="datepicker">
                             <div class="input-group-prepend">
@@ -120,10 +126,15 @@
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js"></script>
     <script src="/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.js"></script>
+    <script src="/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script>
     <script src="/assets/global/js/Plugin/bootstrap-datepicker.js"></script>
     <script>
       $('.input-daterange').datepicker({
         format: 'yyyy-mm-dd',
+        language: 'zh-CN',
+        autoclose: true,
+        todayHighlight: true,
+        clearBtn: true
       });
       //回车检测
       $(document).on('keypress', 'input', function(e) {
@@ -135,8 +146,13 @@
 
       // 搜索
       function Search() {
-        window.location.href = '{{route('admin.log.traffic')}}?port=' + $('#port').val() + '&user_id=' + $('#user_id').val() + '&email=' + $('#email').val()
-            + '&nodeId=' + $('#nodeId option:selected').val() + '&startTime=' + $('#start').val() + '&endTime=' + $('#end').val()
+        window.location.href = '{{route('admin.log.traffic')}}?port=' + $('#port').val() 
+            + '&user_id=' + $('#user_id').val() 
+            + '&email=' + $('#email').val()
+            + '&nodeId=' + $('#nodeId option:selected').val() 
+            + '&is_abnormal=' + $('#is_abnormal').val()
+            + '&startTime=' + $('#start').val() 
+            + '&endTime=' + $('#end').val()
             + '&last_hours=' + $('#last_hours').val();
       }
     </script>

+ 8 - 1
resources/views/admin/logs/userBanHistory.blade.php

@@ -13,6 +13,13 @@
                     <div class="form-group col-lg-3 col-sm-6">
                         <input type="text" class="form-control" name="email" id="email" value="{{Request::input('email')}}" placeholder="用户名"/>
                     </div>
+                    <div class="form-group col-lg-3 col-sm-6">
+                        <select class="form-control" name="is_banned" id="is_banned">
+                            <option value="">全部记录</option>
+                            <option value="1" {{Request::input('is_banned') == '1' ? 'selected' : ''}}>仅显示封禁记录</option>
+                            <option value="0" {{Request::input('is_banned') == '0' ? 'selected' : ''}}>仅显示解封记录</option>
+                        </select>
+                    </div>
                     <div class="form-group col-lg-2 col-sm-6 btn-group">
                         <button class="btn btn-primary" onclick="Search()">搜 索</button>
                         <a href="{{route('admin.log.ban')}}" class="btn btn-danger">{{trans('common.reset')}}</a>
@@ -84,7 +91,7 @@
 
       // 搜索
       function Search() {
-        window.location.href = '{{route('admin.log.ban')}}?email=' + $('#email').val();
+        window.location.href = '{{route('admin.log.ban')}}?email=' + $('#email').val() + '&is_banned=' + $('#is_banned').val();
       }
     </script>
 @endsection

+ 11 - 0
resources/views/admin/user/index.blade.php

@@ -215,6 +215,9 @@
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js"></script>
     <script src="/assets/custom/clipboardjs/clipboard.min.js"></script>
+    <script src="/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.js"></script>
+    <script src="/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script>
+    <script src="/assets/global/js/Plugin/bootstrap-datepicker.js"></script>
     <script>
       $(document).ready(function() {
         $('#group').val({{Request::input('group')}});
@@ -349,5 +352,13 @@
           showConfirmButton: false,
         });
       });
+
+      $('.input-daterange, .datepicker').datepicker({
+          format: 'yyyy-mm-dd',
+          language: 'zh-CN',
+          autoclose: true,
+          todayHighlight: true,
+          clearBtn: true
+      });
     </script>
 @endsection

+ 30 - 23
resources/views/admin/user/info.blade.php

@@ -345,40 +345,47 @@
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-select/bootstrap-select.min.js"></script>
     <script src="/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.js"></script>
-    <script src="/assets/global/vendor/bootstrap-datetimepicker/js/bootstrap-datetimepicker.js"></script>
+    <script src="/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script>
     <script src="/assets/global/js/Plugin/bootstrap-select.js"></script>
     <script src="/assets/global/js/Plugin/bootstrap-datepicker.js"></script>
+    <script src="/assets/global/vendor/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js"></script>
+    <script src="/assets/global/vendor/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js"></script>
     <script>
       $(document).ready(function() {
           @isset($user)
           $('#username').val('{{$user->username}}');
-        $('#email').val('{{$user->email}}');
-        $('#level').selectpicker('val', '{{$user->level}}');
-        $('#group').selectpicker('val', '{{$user->user_group_id}}');
-        $('#invite_num').val('{{$user->invite_num}}');
-        $('#reset_time').val('{{$user->reset_time}}');
-        $('#expired_at').val('{{$user->expired_at}}');
-        $("input[name='status'][value='{{$user->status}}']").click();
-        $('#wechat').val('{{$user->wechat}}');
-        $('#qq').val('{{$user->qq}}');
-        $('#remark').val('{{$user->remark}}');
-        $('#port').val('{{$user->port}}');
-        $('#passwd').val('{{$user->passwd}}');
-        $('#method').selectpicker('val', '{{$user->method}}');
-        $('#transfer_enable').val('{{$user->transfer_enable/GB}}');
-        $("input[name='enable'][value='{{$user->enable}}']").click();
-        $('#protocol').selectpicker('val', '{{$user->protocol}}');
-        $('#obfs').selectpicker('val', '{{$user->obfs}}');
-        $('#speed_limit').val('{{$user->speed_limit}}');
-        $('#uuid').val('{{$user->vmess_id}}');
-        $('#roles').selectpicker('val', @json($user->roles()->pluck('name')));
+          $('#email').val('{{$user->email}}');
+          $('#level').selectpicker('val', '{{$user->level}}');
+          $('#group').selectpicker('val', '{{$user->user_group_id}}');
+          $('#invite_num').val('{{$user->invite_num}}');
+          $('#reset_time').val('{{$user->reset_time}}');
+          $('#expired_at').val('{{$user->expired_at}}');
+          $("input[name='status'][value='{{$user->status}}']").click();
+          $('#wechat').val('{{$user->wechat}}');
+          $('#qq').val('{{$user->qq}}');
+          $('#remark').val('{{$user->remark}}');
+          $('#port').val('{{$user->port}}');
+          $('#passwd').val('{{$user->passwd}}');
+          $('#method').selectpicker('val', '{{$user->method}}');
+          $('#transfer_enable').val('{{$user->transfer_enable/GB}}');
+          $("input[name='enable'][value='{{$user->enable}}']").click();
+          $('#protocol').selectpicker('val', '{{$user->protocol}}');
+          $('#obfs').selectpicker('val', '{{$user->obfs}}');
+          $('#speed_limit').val('{{$user->speed_limit}}');
+          $('#uuid').val('{{$user->vmess_id}}');
+          $('#roles').selectpicker('val', @json($user->roles()->pluck('name')));
           @else
           $('#level').selectpicker('val', '0');
           @endisset
       });
 
-      $('.input-daterange>input').datetimepicker({
-        format: 'yyyy-mm-dd hh:ii:ss', language : 'zh-CN',todayBtn:  1,weekStart: 1,
+      // 优化时间选择器为中文
+      $('#reset_time, #expired_at').datetimepicker({
+        format: 'yyyy-mm-dd hh:ii:ss',
+        language: 'zh-CN',
+        autoclose: true,
+        todayHighlight: true,
+        clearBtn: true
       });
 
       @isset($user)