Bläddra i källkod

更新日期格式

兔姬桑 3 år sedan
förälder
incheckning
2b5a2614a2

+ 0 - 8
app/Components/Helpers.php

@@ -11,7 +11,6 @@ use App\Models\UserCreditLog;
 use App\Models\UserDataModifyLog;
 use App\Models\UserLoginLog;
 use App\Models\UserSubscribe;
-use DateTime;
 use Log;
 use Str;
 
@@ -139,13 +138,6 @@ class Helpers
         return $config->name ?? 'plain';
     }
 
-    public static function daysToNow($date): int
-    {
-        $calculate = (new DateTime())->diff(new DateTime($date));
-
-        return $calculate->invert ? -$calculate->days : $calculate->days;
-    }
-
     /**
      * 添加通知推送日志.
      *

+ 3 - 2
app/Console/Commands/DailyJob.php

@@ -114,8 +114,9 @@ class DailyJob extends Command
                     // 重置流量与重置日期
                     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_time ?: '【无】'));
+                        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.'] 流量重置失败');
                     }

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

@@ -35,7 +35,7 @@ class UserExpireWarning extends Command
                     if (filter_var($user->username, FILTER_VALIDATE_EMAIL) === false) { // 用户账号不是邮箱的跳过
                         continue;
                     }
-                    $user->notify(new AccountExpire($user->expired_at));
+                    $user->notify(new AccountExpire($user->expired_at->diffInDays()));
                 }
             });
     }

+ 278 - 0
app/Helpers/ClientConfig.php

@@ -0,0 +1,278 @@
+<?php
+
+namespace App\Helpers;
+
+use App\Components\Client\Clash;
+use App\Components\Client\QuantumultX;
+use App\Components\Client\Surfboard;
+use App\Components\Client\Surge;
+use App\Components\Client\URLSchemes;
+use App\Components\Client\V2rayN;
+use App\Models\User;
+use File;
+use Symfony\Component\Yaml\Yaml;
+
+trait ClientConfig
+{
+    private function clientConfig(array $servers, string $target)
+    {
+        if (str_contains($target, 'quantumult x')) {
+            return $this->quantumultX(self::$user, $servers);
+        }
+        if (str_contains($target, 'quantumult')) {
+            return $this->quantumult(self::$user, $servers);
+        }
+        if (str_contains($target, 'clash')) {
+            return $this->clash($servers);
+        }
+        if (str_contains($target, 'bob_vpn')) {
+            return $this->clash($servers, 'bob');
+        }
+        if (str_contains($target, 'surfboard')) {
+            return $this->surfboard(self::$user, $servers);
+        }
+        if (str_contains($target, 'surge')) {
+            return $this->surge($target, self::$user, $servers);
+        }
+        if (str_contains($target, 'shadowrocket')) {
+            return $this->shadowrocket(self::$user, $servers);
+        }
+        if (str_contains($target, 'v2rayn')) {
+            return $this->v2rayN(self::$user, $servers);
+        }
+        if (str_contains($target, 'v2rayng')) {
+            return $this->v2rayN(self::$user, $servers);
+        }
+        if (str_contains($target, 'v2rayu')) {
+            return $this->v2rayN(self::$user, $servers);
+        }
+//            if (strpos($target, 'shadowsocks') !== false) {
+//                exit($this->shaodowsocksSIP008($servers));
+//            }
+        return $this->origin($servers);
+    }
+
+    private function quantumultX(User $user, array $servers = []): string
+    {
+        $uri = '';
+        if (sysConfig('is_custom_subscribe')) {
+            header("subscription-userinfo: upload={$user->u}; download={$user->d}; total={$user->transfer_enable}; expire={$user->expiration_date}");
+        }
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $uri .= QuantumultX::buildShadowsocks($server);
+            }
+            if ($server['type'] === 'shadowsocksr') {
+                $uri .= QuantumultX::buildShadowsocksr($server);
+            }
+            if ($server['type'] === 'v2ray') {
+                $uri .= QuantumultX::buildVmess($server);
+            }
+            if ($server['type'] === 'trojan') {
+                $uri .= QuantumultX::buildTrojan($server);
+            }
+        }
+
+        return base64_encode($uri);
+    }
+
+    private function quantumult(User $user, array $servers = []): string
+    {
+        if (sysConfig('is_custom_subscribe')) {
+            header("subscription-userinfo: upload={$user->u}; download={$user->d}; total={$user->transfer_enable}; expire={$user->expiration_date}");
+        }
+
+        return $this->origin($servers);
+    }
+
+    private function origin(array $servers = [], bool $encode = true): string
+    {
+        $uri = '';
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $uri .= URLSchemes::buildShadowsocks($server);
+            }
+            if ($server['type'] === 'shadowsocksr') {
+                $uri .= URLSchemes::buildShadowsocksr($server);
+            }
+            if ($server['type'] === 'v2ray') {
+                $uri .= URLSchemes::buildVmess($server);
+            }
+            if ($server['type'] === 'trojan') {
+                $uri .= URLSchemes::buildTrojan($server);
+            }
+        }
+
+        return $encode ? base64_encode($uri) : $uri;
+    }
+
+    private function clash(array $servers, $client = false)
+    {
+        $custom_path = '/resources/rules/custom.clash.yaml';
+        if ($client === 'bob') {
+            $file_path = '/resources/rules/bob.clash.yaml';
+        } elseif (File::exists(base_path().$custom_path)) {
+            $file_path = $custom_path;
+        } else {
+            $file_path = '/resources/rules/default.clash.yaml';
+        }
+
+        $config = Yaml::parseFile(base_path().$file_path);
+
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $proxy[] = Clash::buildShadowsocks($server);
+                $proxies[] = $server['name'];
+            }
+            if ($server['type'] === 'shadowsocksr') {
+                $proxy[] = Clash::buildShadowsocksr($server);
+                $proxies[] = $server['name'];
+            }
+            if ($server['type'] === 'v2ray') {
+                $proxy[] = Clash::buildVmess($server);
+                $proxies[] = $server['name'];
+            }
+            if ($server['type'] === 'trojan') {
+                $proxy[] = Clash::buildTrojan($server);
+                $proxies[] = $server['name'];
+            }
+        }
+
+        $config['proxies'] = array_merge($config['proxies'] ?: [], $proxy ?? []);
+        foreach ($config['proxy-groups'] as $k => $v) {
+            if (! is_array($config['proxy-groups'][$k]['proxies'])) {
+                continue;
+            }
+            $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies ?? []);
+        }
+
+        return str_replace('$app_name', sysConfig('website_name'), Yaml::dump($config));
+    }
+
+    private function surfboard(User $user, array $servers = [])
+    {
+        $proxies = '';
+        $proxyGroup = '';
+
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $proxies .= Surfboard::buildShadowsocks($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+            if ($server['type'] === 'v2ray') {
+                $proxies .= Surfboard::buildVmess($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+        }
+
+        $defaultConfig = base_path().'/resources/rules/default.surfboard.conf';
+        $customConfig = base_path().'/resources/rules/custom.surfboard.conf';
+        if (File::exists($customConfig)) {
+            $config = file_get_contents($customConfig);
+        } else {
+            $config = file_get_contents($defaultConfig);
+        }
+
+        // Subscription link
+        $subsURL = route('sub', $user->subscribe->code);
+
+        return str_replace(['$subs_link', '$proxies', '$proxy_group'], [$subsURL, $proxies, rtrim($proxyGroup, ', ')], $config);
+    }
+
+    private function surge(string $target, User $user, array $servers = [])
+    {
+        $proxies = '';
+        $proxyGroup = '';
+
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $proxies .= Surge::buildShadowsocks($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+            if ($server['type'] === 'v2ray') {
+                $proxies .= Surge::buildVmess($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+            if ($server['type'] === 'trojan') {
+                $proxies .= Surge::buildTrojan($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+        }
+
+        if (str_contains($target, 'list')) {
+            return $proxies;
+        }
+
+        $defaultConfig = base_path().'/resources/rules/default.surge.conf';
+        $customConfig = base_path().'/resources/rules/custom.surge.conf';
+        if (File::exists($customConfig)) {
+            $config = file_get_contents($customConfig);
+        } else {
+            $config = file_get_contents($defaultConfig);
+        }
+
+        // Subscription link
+        $subsURL = route('sub', $user->subscribe->code);
+
+        return str_replace(['$subs_link', '$proxies', '$proxy_group'], [$subsURL, $proxies, rtrim($proxyGroup, ', ')], $config);
+    }
+
+    private function shadowrocket(User $user, array $servers = []): string
+    {
+        //display remaining traffic and expire date
+        $uri = '';
+        if (sysConfig('is_custom_subscribe')) {
+            $upload = flowAutoShow($user->u);
+            $download = flowAutoShow($user->d);
+            $totalTraffic = flowAutoShow($user->transfer_enable);
+            $uri = "STATUS=📤:{$upload}📥:{$download}⏳:{$totalTraffic}📅:{$user->expiration_date}\r\n";
+        }
+        $uri .= $this->origin($servers, false);
+
+        return base64_encode($uri);
+    }
+
+    private function v2rayN(User $user, array $servers)
+    {
+        $uri = '';
+        if (sysConfig('is_custom_subscribe')) {
+            $text = '';
+            if ($user->expiration_date > date('Y-m-d')) {
+                if ($user->transfer_enable === 0) {
+                    $text .= '剩余流量:0';
+                } else {
+                    $text .= '剩余流量:'.flowAutoShow($user->transfer_enable);
+                }
+                $text .= ', 过期时间:'.$user->expiration_date;
+            } else {
+                $text .= '账户已过期,请续费后使用';
+            }
+            $uri .= $this->failedProxyReturn($text, 2);
+        }
+
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocksr') {
+                $uri .= V2rayN::buildShadowsocksr($server);
+            }
+            if ($server['type'] === 'v2ray') {
+                $uri .= V2rayN::buildVmess($server);
+            }
+            if ($server['type'] === 'trojan') {
+                $uri .= V2rayN::buildTrojan($server);
+            }
+        }
+
+        return base64_encode($uri);
+    }
+
+    private function shaodowsocksSIP008(array $servers): string
+    {
+        foreach ($servers as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $configs[] = URLSchemes::buildShadowsocksSIP008($server);
+            }
+        }
+
+        return json_encode(['version' => 1, 'remark' => sysConfig('website_name'), 'servers' => $configs ?? []], JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
+    }
+}

+ 1 - 1
app/Http/Controllers/AuthController.php

@@ -277,7 +277,7 @@ class AuthController extends Controller
             // 则直接给推荐人加流量
             if ($inviter_id) {
                 $referralUser = User::find($inviter_id);
-                if ($referralUser && $referralUser->expired_at >= date('Y-m-d')) {
+                if ($referralUser && $referralUser->expiration_date >= date('Y-m-d')) {
                     $referralUser->incrementData(sysConfig('referral_traffic') * MB);
                 }
             }

+ 1 - 1
app/Http/Controllers/User/AffiliateController.php

@@ -44,7 +44,7 @@ class AffiliateController extends Controller
     public function extractMoney(): JsonResponse
     {
         // 判断账户是否过期
-        if (Auth::getUser()->expired_at < date('Y-m-d')) {
+        if (Auth::getUser()->expiration_date < date('Y-m-d')) {
             return Response::json(['status' => 'fail', 'title' => trans('user.referral.failed'), 'message' => trans('user.referral.msg.account')]);
         }
 

+ 2 - 3
app/Http/Controllers/User/SubscribeController.php

@@ -55,7 +55,7 @@ class SubscribeController extends Controller
                 return $this->failed(trans('errors.subscribe.out'));
             }
 
-            if ($user->expired_at < date('Y-m-d')) {
+            if ($user->expiration_date < date('Y-m-d')) {
                 return $this->failed(trans('errors.subscribe.expired'));
             }
 
@@ -127,9 +127,8 @@ class SubscribeController extends Controller
         return $result.PHP_EOL;
     }
 
-    // 写入订阅访问日志
     private function subscribeLog($subscribeId, $ip, $headers): void
-    {
+    { // 写入订阅访问日志
         $log = new UserSubscribeLog();
         $log->user_subscribe_id = $subscribeId;
         $log->request_ip = $ip;

+ 6 - 7
app/Http/Controllers/UserController.php

@@ -43,7 +43,6 @@ class UserController extends Controller
         $totalTransfer = $user->transfer_enable;
         $usedTransfer = $user->used_traffic;
         $unusedTraffic = max($totalTransfer - $usedTransfer, 0);
-        $expireTime = $user->expired_at;
 
         $nodes = $user->nodes()->get();
         $subType = [];
@@ -58,10 +57,10 @@ class UserController extends Controller
         }
 
         return view('user.index', array_merge([
-            'remainDays'       => Helpers::daysToNow($expireTime),
-            'resetDays'        => $user->reset_time ? Helpers::daysToNow($user->reset_time) : null,
+            'remainDays'       => now()->diffInDays($user->expired_at, false),
+            'resetDays'        => $user->reset_time ? now()->diffInDays($user->reset_time, false) : null,
             'unusedTraffic'    => flowAutoShow($unusedTraffic),
-            'expireTime'       => $expireTime,
+            'expireTime'       => $user->expiration_date,
             'banedTime'        => $user->ban_time,
             'unusedPercent'    => $totalTransfer > 0 ? round($unusedTraffic / $totalTransfer, 2) * 100 : 0,
             'announcements'    => Article::type(2)->take(5)->latest()->Paginate(1), // 公告
@@ -217,7 +216,7 @@ class UserController extends Controller
             'chargeGoodsList' => Goods::type(3)->whereStatus(1)->orderBy('price')->get(),
             'goodsList'       => $goodsList,
             'renewTraffic'    => $renewPrice->renew ?? 0,
-            'dataPlusDays'    => $dataPlusDays > date('Y-m-d') ? Helpers::daysToNow($dataPlusDays) : 0,
+            'dataPlusDays'    => $dataPlusDays > date('Y-m-d') ? $dataPlusDays->diffInDays() : 0,
         ]);
     }
 
@@ -341,7 +340,7 @@ class UserController extends Controller
 
         return view('user.replyTicket', [
             'ticket'    => $ticket,
-            'replyList' => $ticket->reply()->with('user')->oldest()->get(),
+            'replyList' => $ticket->reply()->with('ticket:id,status', 'admin:id,username,qq', 'user:id,username,qq')->oldest()->get(),
         ]);
     }
 
@@ -428,7 +427,7 @@ class UserController extends Controller
         $dataPlusDays = $user->reset_time ?? $user->expired_at;
 
         return view('user.buy', [
-            'dataPlusDays' => $dataPlusDays > date('Y-m-d') ? Helpers::daysToNow($dataPlusDays) : 0,
+            'dataPlusDays' => $dataPlusDays > date('Y-m-d') ? $dataPlusDays->diffInDays() : 0,
             'activePlan'   => Order::userActivePlan()->exists(),
             'goods'        => $good,
         ]);

+ 21 - 20
app/Models/User.php

@@ -22,7 +22,6 @@ class User extends Authenticatable
     public $sortable = ['id', 'credit', 'port', 't', 'expired_at'];
     protected $table = 'user';
     protected $casts = ['expired_at' => 'date:Y-m-d', 'reset_time' => 'date:Y-m-d', 'ban_time' => 'date:Y-m-d'];
-    protected $dates = ['expired_at', 'reset_time'];
     protected $guarded = [];
 
     public function routeNotificationForMail($notification)
@@ -40,6 +39,16 @@ class User extends Authenticatable
         return $this->d + $this->u;
     }
 
+    public function getExpirationDateAttribute()
+    {
+        return $this->attributes['expired_at'];
+    }
+
+    public function getResetDateAttribute()
+    {
+        return $this->attributes['reset_time'];
+    }
+
     public function getTelegramUserIdAttribute()
     {
         $telegram = $this->userAuths()->whereType('telegram')->first();
@@ -204,16 +213,6 @@ class User extends Authenticatable
         return $this->attributes['speed_limit'] / Mbps;
     }
 
-    public function getExpiredAtAttribute()
-    {
-        return $this->attributes['expired_at'];
-    }
-
-    public function getResetTimeAttribute()
-    {
-        return $this->attributes['reset_time'];
-    }
-
     public function setPasswordAttribute($password)
     {
         return $this->attributes['password'] = Hash::make($password);
@@ -289,18 +288,20 @@ class User extends Authenticatable
         return $this->save();
     }
 
-    public function expired_status(): int
+    public function expiration_status(): int
     {
-        $expired_status = 2; // 大于一个月过期
-        if ($this->expired_at < date('Y-m-d')) {
-            $expired_status = -1; // 已过期
-        } elseif ($this->expired_at === date('Y-m-d')) {
-            $expired_status = 0; // 今天过期
-        } elseif ($this->expired_at > date('Y-m-d') && $this->expired_at <= date('Y-m-d', strtotime('30 days'))) {
-            $expired_status = 1; // 最近一个月过期
+        $today = date('Y-m-d');
+        $nextMonth = date('Y-m-d', strtotime('next month'));
+
+        if ($this->expiration_date < $today) {
+            $status = 0; // 已过期
+        } elseif ($this->expiration_date === $today) {
+            $status = 1; // 今日过期
+        } elseif ($this->expiration_date <= $nextMonth) {
+            $status = 2; // 一个月内过期
         }
 
-        return $expired_status;
+        return $status ?? 3;
     }
 
     public function isTrafficWarning(): bool

+ 2 - 2
app/Notifications/AccountExpire.php

@@ -13,9 +13,9 @@ class AccountExpire extends Notification implements ShouldQueue
 
     private $days;
 
-    public function __construct($expired_at)
+    public function __construct($expire_days)
     {
-        $this->days = now()->diffInDays($expired_at);
+        $this->days = $expire_days;
     }
 
     public function via($notifiable)

+ 71 - 75
resources/views/admin/ticket/reply.blade.php

@@ -84,21 +84,17 @@
                             <dt class="col-sm-3">流量</dt>
                             <dd class="col-sm-9">{{flowAutoShow($user->used_traffic)}} / {{$user->transfer_enable_formatted}}</dd>
                             <dt class="col-sm-3">重置日期</dt>
-                            <dd class="col-sm-9">{{$user->reset_time ?? '无'}}</dd>
+                            <dd class="col-sm-9">{{$user->reset_date ?? '无'}}</dd>
                             <dt class="col-sm-3">最近使用</dt>
                             <dd class="col-sm-9">
                                 {{$user->t? date('Y-m-d H:i', $user->t): '未使用'}}
                             </dd>
                             <dt class="col-sm-3">过期日期</dt>
                             <dd class="col-sm-9">
-                                @if ($user->expired_at < date('Y-m-d'))
-                                    <span class="badge badge-lg badge-danger"> {{$user->expired_at}} </span>
-                                @elseif ($user->expired_at === date('Y-m-d'))
-                                    <span class="badge badge-lg badge-warning"> {{$user->expired_at}} </span>
-                                @elseif ($user->expired_at <= date('Y-m-d', strtotime('30 days')))
-                                    <span class="badge badge-lg badge-default"> {{$user->expired_at}} </span>
+                                @if($user->expiration_status() !== 3)
+                                    <span class="badge badge-lg badge-{{['danger','warning','default'][$user->expiration_status()]}}"> {{ $user->expiration_date }} </span>
                                 @else
-                                    {{$user->expired_at}}
+                                    {{ $user->expiration_date }}
                                 @endif
                             </dd>
                             <dt class="col-sm-3">备注</dt>
@@ -146,85 +142,85 @@
         @can('admin.ticket.destroy')
         // 关闭工单
         function closeTicket() {
-            swal.fire({
-                title: '确定关闭工单?',
-                icon: 'question',
-                showCancelButton: true,
-                cancelButtonText: '{{trans('common.close')}}',
-                confirmButtonText: '{{trans('common.confirm')}}',
-            }).then((result) => {
-                if (result.value) {
-                    $.ajax({
-                        method: 'DELETE',
-                        url: '{{route('admin.ticket.destroy', $ticket->id)}}',
-                        async: true,
-                        data: {_token: '{{csrf_token()}}'},
-                        dataType: 'json',
-                        success: function(ret) {
-                            if (ret.status === 'success') {
-                                swal.fire({
-                                    title: ret.message,
-                                    icon: 'success',
-                                    timer: 1000,
-                                    showConfirmButton: false,
-                                }).then(() => window.location.href = '{{route('admin.ticket.index')}}');
-                            } else {
-                                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                            }
-                        },
-                        error: function() {
-                            swal.fire({title: '{{trans('user.ticket.error')}}', icon: 'error'});
-                        },
-                    });
-                }
-            });
+          swal.fire({
+            title: '确定关闭工单?',
+            icon: 'question',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('common.close')}}',
+            confirmButtonText: '{{trans('common.confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.ticket.destroy', $ticket->id)}}',
+                async: true,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({
+                      title: ret.message,
+                      icon: 'success',
+                      timer: 1000,
+                      showConfirmButton: false,
+                    }).then(() => window.location.href = '{{route('admin.ticket.index')}}');
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+                error: function() {
+                  swal.fire({title: '{{trans('user.ticket.error')}}', icon: 'error'});
+                },
+              });
+            }
+          });
         }
         @endcan
 
         @can('admin.ticket.update')
         //回车检测
         $(document).on('keypress', 'input', function(e) {
-            if (e.which === 13) {
-                replyTicket();
-                return false;
-            }
+          if (e.which === 13) {
+            replyTicket();
+            return false;
+          }
         });
 
         // 回复工单
         function replyTicket() {
-            const content = document.getElementById('editor').value;
+          const content = document.getElementById('editor').value;
 
-            if (content.trim() === '') {
-                swal.fire({title: '{{trans('validation.required', ['attribute' => trans('validation.attributes.content')])}}!', icon: 'warning', timer: 1500});
-                return false;
+          if (content.trim() === '') {
+            swal.fire({title: '{{trans('validation.required', ['attribute' => trans('validation.attributes.content')])}}!', icon: 'warning', timer: 1500});
+            return false;
+          }
+          swal.fire({
+            title: '{{trans('user.ticket.reply_confirm')}}',
+            icon: 'question',
+            allowEnterKey: false,
+            showCancelButton: true,
+            cancelButtonText: '{{trans('common.close')}}',
+            confirmButtonText: '{{trans('common.confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'PUT',
+                url: '{{route('admin.ticket.update', $ticket)}}',
+                data: {_token: '{{csrf_token()}}', content: content},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+                error: function() {
+                  swal.fire({title: '未知错误!请查看运行日志', icon: 'error'});
+                },
+              });
             }
-            swal.fire({
-                title: '{{trans('user.ticket.reply_confirm')}}',
-                icon: 'question',
-                allowEnterKey: false,
-                showCancelButton: true,
-                cancelButtonText: '{{trans('common.close')}}',
-                confirmButtonText: '{{trans('common.confirm')}}',
-            }).then((result) => {
-                if (result.value) {
-                    $.ajax({
-                        method: 'PUT',
-                        url: '{{route('admin.ticket.update', $ticket)}}',
-                        data: {_token: '{{csrf_token()}}', content: content},
-                        dataType: 'json',
-                        success: function(ret) {
-                            if (ret.status === 'success') {
-                                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                            } else {
-                                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                            }
-                        },
-                        error: function() {
-                            swal.fire({title: '未知错误!请查看运行日志', icon: 'error'});
-                        },
-                    });
-                }
-            });
+          });
         }
         @endcan
     </script>

+ 141 - 145
resources/views/admin/user/index.blade.php

@@ -118,14 +118,10 @@
                             <td> {{flowAutoShow($user->used_traffic)}} / {{$user->transfer_enable_formatted}} </td>
                             <td> {{$user->t? date('Y-m-d H:i', $user->t): '未使用'}} </td>
                             <td>
-                                @if ($user->expired_at < date('Y-m-d'))
-                                    <span class="badge badge-lg badge-danger"> {{$user->expired_at}} </span>
-                                @elseif ($user->expired_at === date('Y-m-d'))
-                                    <span class="badge badge-lg badge-warning"> {{$user->expired_at}} </span>
-                                @elseif ($user->expired_at <= date('Y-m-d', strtotime('30 days')))
-                                    <span class="badge badge-lg badge-default"> {{$user->expired_at}} </span>
+                                @if($user->expiration_status() !== 3)
+                                    <span class="badge badge-lg badge-{{['danger','warning','default'][$user->expiration_status()]}}"> {{ $user->expiration_date }} </span>
                                 @else
-                                    {{$user->expired_at}}
+                                    {{ $user->expiration_date }}
                                 @endif
                             </td>
                             <td>
@@ -222,156 +218,156 @@
     <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>
-        $(document).ready(function() {
-            $('#user_group_id').val({{Request::query('user_group_id')}});
-            $('#level').val({{Request::query('level')}});
-            $('#status').val({{Request::query('status')}});
-            $('#enable').val({{Request::query('enable')}});
-            $('select').on('change', function() { this.form.submit(); });
-        });
+      $(document).ready(function() {
+        $('#user_group_id').val({{Request::query('user_group_id')}});
+        $('#level').val({{Request::query('level')}});
+        $('#status').val({{Request::query('status')}});
+        $('#enable').val({{Request::query('enable')}});
+        $('select').on('change', function() { this.form.submit(); });
+      });
 
-        @can('admin.user.batch')
-        // 批量生成账号
-        function batchAddUsers() {
-            swal.fire({
-                title: '用户生成数量',
-                input: 'range',
-                inputAttributes: {min: 1, max: 10},
-                inputValue: 1,
-                icon: 'question',
-                showCancelButton: true,
-                cancelButtonText: '{{trans('common.close')}}',
-                confirmButtonText: '{{trans('common.confirm')}}',
-            }).then((result) => {
-                if (result.value) {
-                    $.post('{{route('admin.user.batch')}}', {_token: '{{csrf_token()}}', amount: result.value}, function(ret) {
-                        if (ret.status === 'success') {
-                            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                        } else {
-                            swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                        }
-                    });
-                }
+      @can('admin.user.batch')
+      // 批量生成账号
+      function batchAddUsers() {
+        swal.fire({
+          title: '用户生成数量',
+          input: 'range',
+          inputAttributes: {min: 1, max: 10},
+          inputValue: 1,
+          icon: 'question',
+          showCancelButton: true,
+          cancelButtonText: '{{trans('common.close')}}',
+          confirmButtonText: '{{trans('common.confirm')}}',
+        }).then((result) => {
+          if (result.value) {
+            $.post('{{route('admin.user.batch')}}', {_token: '{{csrf_token()}}', amount: result.value}, function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+              }
             });
-        }
-        @endcan
-
-        @can('admin.user.destroy')
-        // 删除账号
-        function delUser(url, username) {
-            swal.fire({
-                title: '{{trans('common.warning')}}',
-                text: '确定删除用户 【' + username + '】 ?',
-                icon: 'warning',
-                showCancelButton: true,
-                cancelButtonText: '{{trans('common.close')}}',
-                confirmButtonText: '{{trans('common.confirm')}}',
-            }).then((result) => {
-                if (result.value) {
-                    $.ajax({
-                        method: 'DELETE',
-                        url: url,
-                        data: {_token: '{{csrf_token()}}'},
-                        dataType: 'json',
-                        success: function(ret) {
-                            if (ret.status === 'success') {
-                                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                            } else {
-                                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                            }
-                        },
-                    });
-                }
-            });
-        }
-        @endcan
-
-        @can('admin.user.reset')
-        // 重置流量
-        function resetTraffic(id, username) {
-            swal.fire({
-                title: '{{trans('common.warning')}}',
-                text: '确定重置 【' + username + '】 流量吗?',
-                icon: 'warning',
-                showCancelButton: true,
-                cancelButtonText: '{{trans('common.close')}}',
-                confirmButtonText: '{{trans('common.confirm')}}',
-            }).then((result) => {
-                if (result.value) {
-                    $.post('{{route('admin.user.reset', '')}}/' + id, {_token: '{{csrf_token()}}'}, function(ret) {
-                        if (ret.status === 'success') {
-                            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                        } else {
-                            swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                        }
-                    });
-                }
-            });
-        }
-        @endcan
+          }
+        });
+      }
+      @endcan
 
-        @can('admin.user.switch')
-        // 切换用户身份
-        function switchToUser(id) {
-            $.post('{{route('admin.user.switch', '')}}/' + id, {_token: '{{csrf_token()}}'}, function(ret) {
+      @can('admin.user.destroy')
+      // 删除账号
+      function delUser(url, username) {
+        swal.fire({
+          title: '{{trans('common.warning')}}',
+          text: '确定删除用户 【' + username + '】 ?',
+          icon: 'warning',
+          showCancelButton: true,
+          cancelButtonText: '{{trans('common.close')}}',
+          confirmButtonText: '{{trans('common.confirm')}}',
+        }).then((result) => {
+          if (result.value) {
+            $.ajax({
+              method: 'DELETE',
+              url: url,
+              data: {_token: '{{csrf_token()}}'},
+              dataType: 'json',
+              success: function(ret) {
                 if (ret.status === 'success') {
-                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.href = '/');
+                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
                 } else {
-                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
                 }
+              },
             });
-        }
-        @endcan
+          }
+        });
+      }
+      @endcan
 
-        @can('admin.user.VNetInfo')
-        // 节点连通性测试
-        function VNetInfo(id) {
-            $.ajax({
-                method: 'POST',
-                url: '{{route('admin.user.VNetInfo', '')}}/' + id,
-                data: {_token: '{{csrf_token()}}'},
-                beforeSend: function() {
-                    $('#vent_' + id).removeClass('wb-link-broken').addClass('wb-loop icon-spin');
-                },
-                success: function(ret) {
-                    if (ret.status === 'success') {
-                        let str = '';
-                        for (let i in ret.data) {
-                            str += '<tr><td>' + ret.data[i]['id'] + '</td><td>' + ret.data[i]['name'] + '</td><td>' + ret.data[i]['avaliable'] + '</td></tr>';
-                        }
-                        swal.fire({
-                            title: ret.title,
-                            icon: 'info',
-                            html: '<table class="my-20"><thead class="thead-default"><tr><th> ID </th><th> 节点 </th> <th> 状态 </th></thead><tbody>' + str + '</tbody></table>',
-                            showConfirmButton: false,
-                        });
-                    } else {
-                        swal.fire({title: ret.title, text: ret.data, icon: 'error'});
-                    }
-                },
-                complete: function() {
-                    $('#vent_' + id).removeClass('wb-loop icon-spin').addClass('wb-link-broken');
-                },
+      @can('admin.user.reset')
+      // 重置流量
+      function resetTraffic(id, username) {
+        swal.fire({
+          title: '{{trans('common.warning')}}',
+          text: '确定重置 【' + username + '】 流量吗?',
+          icon: 'warning',
+          showCancelButton: true,
+          cancelButtonText: '{{trans('common.close')}}',
+          confirmButtonText: '{{trans('common.confirm')}}',
+        }).then((result) => {
+          if (result.value) {
+            $.post('{{route('admin.user.reset', '')}}/' + id, {_token: '{{csrf_token()}}'}, function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+              }
             });
-        }
-        @endcan
+          }
+        });
+      }
+      @endcan
 
-        const clipboard = new ClipboardJS('.copySubscribeLink');
-        clipboard.on('success', function() {
-            swal.fire({
-                title: '{{trans('common.copy.success')}}',
-                icon: 'success',
-                timer: 1000,
-                showConfirmButton: false,
-            });
+      @can('admin.user.switch')
+      // 切换用户身份
+      function switchToUser(id) {
+        $.post('{{route('admin.user.switch', '')}}/' + id, {_token: '{{csrf_token()}}'}, function(ret) {
+          if (ret.status === 'success') {
+            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.href = '/');
+          } else {
+            swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+          }
         });
-        clipboard.on('error', function() {
-            swal.fire({
-                title: '{{trans('common.copy.failed')}}',
-                icon: 'error',
-                timer: 1500,
+      }
+      @endcan
+
+      @can('admin.user.VNetInfo')
+      // 节点连通性测试
+      function VNetInfo(id) {
+        $.ajax({
+          method: 'POST',
+          url: '{{route('admin.user.VNetInfo', '')}}/' + id,
+          data: {_token: '{{csrf_token()}}'},
+          beforeSend: function() {
+            $('#vent_' + id).removeClass('wb-link-broken').addClass('wb-loop icon-spin');
+          },
+          success: function(ret) {
+            if (ret.status === 'success') {
+              let str = '';
+              for (let i in ret.data) {
+                str += '<tr><td>' + ret.data[i]['id'] + '</td><td>' + ret.data[i]['name'] + '</td><td>' + ret.data[i]['avaliable'] + '</td></tr>';
+              }
+              swal.fire({
+                title: ret.title,
+                icon: 'info',
+                html: '<table class="my-20"><thead class="thead-default"><tr><th> ID </th><th> 节点 </th> <th> 状态 </th></thead><tbody>' + str + '</tbody></table>',
                 showConfirmButton: false,
-            });
+              });
+            } else {
+              swal.fire({title: ret.title, text: ret.data, icon: 'error'});
+            }
+          },
+          complete: function() {
+            $('#vent_' + id).removeClass('wb-loop icon-spin').addClass('wb-link-broken');
+          },
+        });
+      }
+      @endcan
+
+      const clipboard = new ClipboardJS('.copySubscribeLink');
+      clipboard.on('success', function() {
+        swal.fire({
+          title: '{{trans('common.copy.success')}}',
+          icon: 'success',
+          timer: 1000,
+          showConfirmButton: false,
+        });
+      });
+      clipboard.on('error', function() {
+        swal.fire({
+          title: '{{trans('common.copy.failed')}}',
+          icon: 'error',
+          timer: 1500,
+          showConfirmButton: false,
         });
+      });
     </script>
 @endsection

+ 2 - 2
resources/views/admin/user/info.blade.php

@@ -346,8 +346,8 @@
             $('#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}}');
+            $('#reset_time').val('{{$user->reset_date}}');
+            $('#expired_at').val('{{$user->expiration_date}}');
             $("input[name='status'][value='{{$user->status}}']").click();
             $('#wechat').val('{{$user->wechat}}');
             $('#qq').val('{{$user->qq}}');

+ 1 - 1
resources/views/components/payment/detail.blade.php

@@ -81,7 +81,7 @@
                                     余额:{{ $user->credit }}
                                 </li>
                                 <li class="list-group-item">
-                                    过期时间:{{ $user->expired_at }}
+                                    过期时间:{{ $user->expiration_date }}
                                 </li>
                             </ul>
                         </div>

+ 1 - 1
resources/views/components/ticket/reply.blade.php

@@ -81,7 +81,7 @@
                                     余额:{{ $user->credit }}
                                 </li>
                                 <li class="list-group-item">
-                                    过期时间:{{ $user->expired_at }}
+                                    过期时间:{{ $user->expiration_date }}
                                 </li>
                             </ul>
                         </div>