Преглед изворни кода

Optimize User Information Handling

BrettonYe пре 9 месеци
родитељ
комит
7d1d59be42

+ 6 - 3
app/Console/Commands/TaskAuto.php

@@ -9,6 +9,7 @@ use App\Models\Order;
 use App\Models\User;
 use App\Models\UserSubscribe;
 use App\Models\VerifyCode;
+use App\Services\UserService;
 use Illuminate\Console\Command;
 use Illuminate\Database\Eloquent\Builder;
 use Log;
@@ -106,11 +107,13 @@ class TaskAuto extends Command
         if (sysConfig('is_traffic_ban')) {
             $trafficBanTime = sysConfig('traffic_ban_time');
             $ban_time = strtotime($trafficBanTime.' minutes');
+            $userService = new UserService;
 
             User::activeUser()->whereBanTime(null)->where('t', '>=', strtotime('-5 minutes')) // 只检测最近5分钟有流量使用的用户
-                ->chunk(config('tasks.chunk'), function ($users) use ($ban_time, $trafficBanTime) {
-                    $users->each(function ($user) use ($ban_time, $trafficBanTime) {
-                        if ($user->isTrafficWarning()) {
+                ->chunk(config('tasks.chunk'), function ($users) use ($userService, $ban_time, $trafficBanTime) {
+                    $users->each(function ($user) use ($userService, $ban_time, $trafficBanTime) {
+                        $userService->setUser($user);
+                        if ($userService->isTrafficWarning()) {
                             $user->update(['enable' => 0, 'ban_time' => $ban_time]);
                             $user->banedLogs()->create(['time' => $trafficBanTime, 'description' => __('[Auto Task] Blocked service: Abnormal traffic within 1 hour')]); // 写入日志
                         }

+ 31 - 28
app/Http/Controllers/Admin/UserController.php

@@ -21,6 +21,7 @@ use Exception;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
+use Illuminate\Support\Collection;
 use Log;
 use Spatie\Permission\Models\Role;
 use Str;
@@ -75,9 +76,9 @@ class UserController extends Controller
         });
 
         return view('admin.user.index', [
-            'userList' => $query->sortable(['id' => 'desc'])->paginate(15)->appends($request->except('page')),
-            'userGroups' => UserGroup::all()->pluck('name', 'id')->toArray(),
-            'levels' => Level::all()->pluck('name', 'level')->toArray(),
+            'userList' => $query->with('subscribe:user_id,code')->sortable(['id' => 'desc'])->paginate(15)->appends($request->except('page')),
+            'userGroups' => UserGroup::pluck('name', 'id'),
+            'levels' => Level::orderBy('level')->pluck('name', 'level'),
         ]);
     }
 
@@ -99,8 +100,8 @@ class UserController extends Controller
 
         $roles = $request->input('roles');
         try {
-            $adminUser = auth()->user();
-            if ($roles && ($adminUser->can('give roles') || (in_array('Super Admin', $roles, true) && $adminUser->hasRole('Super Admin')))) {
+            $editor = auth()->user();
+            if ($roles && ($editor->can('give roles') || (in_array('Super Admin', $roles, true) && $editor->hasRole('Super Admin')))) {
                 // 编辑用户权限, 只有超级管理员才有赋予超级管理的权限
                 $user->assignRole($roles);
             }
@@ -121,32 +122,34 @@ class UserController extends Controller
 
     public function create(): View
     {
-        if (auth()->user()->hasRole('Super Admin')) { // 超级管理员直接获取全部角色
-            $roles = Role::all()->pluck('description', 'name');
-        } elseif (auth()->user()->can('give roles')) { // 有权者只能获得已有角色,防止权限泛滥
-            $roles = auth()->user()->roles()->pluck('description', 'name');
-        }
-
         return view('admin.user.info', [
-            'levels' => Level::orderBy('level')->get(),
-            'userGroups' => UserGroup::orderBy('id')->get(),
-            'roles' => $roles ?? null,
+            'levels' => Level::orderBy('level')->pluck('name', 'level'),
+            'userGroups' => UserGroup::orderBy('id')->pluck('name', 'id'),
+            'roles' => $this->getAvailableRoles(),
         ]);
     }
 
-    public function edit(User $user): View
+    private function getAvailableRoles(): ?Collection
     {
-        if (auth()->user()->hasRole('Super Admin')) { // 超级管理员直接获取全部角色
-            $roles = Role::all()->pluck('description', 'name');
-        } elseif (auth()->user()->can('give roles')) { // 有权者只能获得已有角色,防止权限泛滥
-            $roles = auth()->user()->roles()->pluck('description', 'name');
+        $editor = auth()->user();
+        if ($editor->hasRole('Super Admin')) { // 超级管理员直接获取全部角色
+            return Role::pluck('description', 'name');
+        }
+
+        if ($editor->can('give roles')) { // 有权者只能获得已有角色,防止权限泛滥
+            return $editor->roles()->pluck('description', 'name');
         }
 
+        return null;
+    }
+
+    public function edit(User $user): View
+    {
         return view('admin.user.info', [
             'user' => $user->load('inviter:id,username'),
-            'levels' => Level::orderBy('level')->get(),
-            'userGroups' => UserGroup::orderBy('id')->get(),
-            'roles' => $roles ?? null,
+            'levels' => Level::orderBy('level')->pluck('name', 'level'),
+            'userGroups' => UserGroup::orderBy('id')->pluck('name', 'id'),
+            'roles' => $this->getAvailableRoles(),
         ]);
     }
 
@@ -224,8 +227,8 @@ class UserController extends Controller
         $roles = $request->input('roles');
         try {
             if (isset($roles)) {
-                $adminUser = auth()->user();
-                if ($adminUser->can('give roles') || $adminUser->hasRole('Super Admin')
+                $editor = auth()->user();
+                if ($editor->can('give roles') || $editor->hasRole('Super Admin')
                     || (in_array('Super Admin', $roles, true) && auth()->user()->hasRole('Super Admin'))) {
                     $user->syncRoles($roles);
                 }
@@ -288,12 +291,12 @@ class UserController extends Controller
         ]);
     }
 
-    public function exportProxyConfig(Request $request, User $user): JsonResponse
+    public function exportProxyConfig(Request $request, User $user, ProxyService $proxyService): JsonResponse
     {
-        $proxyServer = new ProxyService($user);
-        $server = $proxyServer->getProxyConfig(Node::findOrFail($request->input('id')));
+        $proxyService->setUser($user);
+        $server = $proxyService->getProxyConfig(Node::findOrFail($request->input('id')));
 
-        return response()->json(['status' => 'success', 'data' => $proxyServer->getUserProxyConfig($server, $request->input('type') !== 'text'), 'title' => $server['type']]);
+        return response()->json(['status' => 'success', 'data' => $proxyService->getUserProxyConfig($server, $request->input('type') !== 'text'), 'title' => $server['type']]);
     }
 
     public function oauth(): View

+ 1 - 0
app/Http/Controllers/Api/Client/AuthController.php

@@ -41,6 +41,7 @@ class AuthController extends Controller
         // 创建新用户
         if ($user = Helpers::addUser($data['username'], $data['password'], MiB * sysConfig('default_traffic'), (int) sysConfig('default_days'), null, $data['nickname'])) {
             auth()->login($user, true);
+            $userService->setUser($user);
 
             return $this->succeed([
                 'token' => $user->createToken('client')->plainTextToken,

+ 13 - 17
app/Http/Controllers/Api/Client/ClientController.php

@@ -49,8 +49,7 @@ class ClientController extends Controller
         $userInfo['plan']['name'] = $user->orders()->activePlan()->latest()->first()->goods->name ?? '无';
         $ann = Article::type(2)->latest()->first();
         $user_expire = now()->diffInDays($user->expired_at, false) < 0;
-        $total = $user->u + $user->d;
-        $transfer_enable = $user->transfer_enable;
+        $used = $user->used_traffic;
         $expired_days = now()->diffInDays($user->expired_at, false);
         $userInfo['class_expire_notice'] = '';
         if ($expired_days < 0) {
@@ -63,13 +62,13 @@ class ClientController extends Controller
             'user' => $userInfo,
             'ssrSubToken' => $user->subscribe->code,
             'user_expire' => $user_expire,
-            'subUrl' => $user->subUrl(),
+            'subUrl' => $user->sub_url,
             'baseUrl' => sysConfig('subscribe_domain') ?? sysConfig('website_url'),
             'ann' => $ann,
             'avatar' => $user->avatar,
-            'usedTraffic' => formatBytes($total),
-            'enableTraffic' => formatBytes($transfer_enable),
-            'unUsedTraffic' => formatBytes($transfer_enable - $total),
+            'usedTraffic' => formatBytes($used),
+            'enableTraffic' => $user->transfer_enable_formatted,
+            'unUsedTraffic' => formatBytes($user->unused_traffic),
             'reset_time' => now()->diffInDays($user->reset_time, false),
             'android_index_button' => config('client.android_index_button'),
         ];
@@ -102,7 +101,7 @@ class ClientController extends Controller
             'arr' => [
                 'todayUsedTraffic' => formatBytes($user->d),
                 'lastUsedTraffic' => formatBytes($user->u),
-                'unUsedTraffic' => formatBytes($user->transfer_enable - $user->d - $user->u),
+                'unUsedTraffic' => formatBytes($user->unused_traffic),
             ],
         ]);
     }
@@ -124,8 +123,6 @@ class ClientController extends Controller
     public function getInvite(): JsonResponse
     {
         $user = auth()->user();
-        $userService = new UserService;
-
         $referral_traffic = formatBytes(sysConfig('referral_traffic'), 'MiB');
         $referral_percent = sysConfig('referral_percent');
         // 邀请码
@@ -136,8 +133,8 @@ class ClientController extends Controller
             'referral_percent' => $referral_percent * 100,
         ]);
 
-        $data['invite_code'] = $code ?? $userService->inviteURI(true);
-        $data['invite_url'] = $userService->inviteURI();
+        $data['invite_code'] = $code ?? $user->invite_code;
+        $data['invite_url'] = $user->invite_url;
         $data['invite_text'] = $data['invite_url'].'&(复制整段文字到浏览器打开即可访问),找梯子最重要的就是稳定,这个已经上线三年了,一直稳定没有被封过,赶紧下载备用吧!'.($code ? '安装后打开填写我的邀请码【'.$code.'】,你还能多得3天会员.' : '');
         // 累计数据
         $data['back_sum'] = ReferralLog::uid()->sum('commission') / 100;
@@ -179,11 +176,11 @@ class ClientController extends Controller
         return $this->succeed(null, null, [200, trans('user.home.attendance.success', ['data' => formatBytes($traffic)])]);
     }
 
-    public function proxyCheck(Request $request): JsonResponse
+    public function proxyCheck(Request $request, ProxyService $proxyService): JsonResponse
     {
         $md5 = $request->get('md5', '');
 
-        $proxy = (new ProxyService)->getProxyCode('clash');
+        $proxy = $proxyService->getProxyCode('clash');
         if (strtolower(md5(json_encode($proxy))) === strtolower($md5)) {
             return $this->succeed(false);
         }
@@ -191,16 +188,15 @@ class ClientController extends Controller
         return $this->succeed(true, ['md5' => strtolower(md5(json_encode($proxy)))]);
     }
 
-    public function downloadProxies(Request $request)
+    public function downloadProxies(Request $request, ProxyService $proxyService)
     {
         $flag = strtolower($request->input('flag') ?? ($request->userAgent() ?? ''));
 
-        return (new ProxyService)->getProxyText($flag === 'v2rayng' ? 'v2rayng' : 'clash', $request->input('type'));
+        return $proxyService->getProxyText($flag === 'v2rayng' ? 'v2rayng' : 'clash', $request->input('type'));
     }
 
-    public function getProxyList(): JsonResponse
+    public function getProxyList(ProxyService $proxyService): JsonResponse
     {
-        $proxyService = new ProxyService;
         $servers = [];
         foreach ($proxyService->getNodeList(null, false) as $node) {
             $server = $proxyService->getProxyConfig($node);

+ 8 - 11
app/Http/Controllers/TelegramController.php

@@ -12,14 +12,15 @@ use StdClass;
 
 class TelegramController extends Controller
 {
-    protected $msg;
+    protected StdClass $msg;
 
     public function webhook(Request $request): void
     {
-        $this->msg = $this->getMessage($request->input());
-        if (! $this->msg) {
+        $msg = $this->getMessage($request->input());
+        if (! $msg) {
             return;
         }
+        $this->msg = $msg;
         try {
             switch ($this->msg->message_type) {
                 case 'send':
@@ -35,7 +36,7 @@ class TelegramController extends Controller
         }
     }
 
-    private function getMessage(array $data)
+    private function getMessage(array $data): false|StdClass
     {
         if (! isset($data['message'])) {
             return false;
@@ -99,7 +100,7 @@ class TelegramController extends Controller
         }
     }
 
-    private function replayTicket($ticketId): void
+    private function replayTicket(int $ticketId): void
     {
         $msg = $this->msg;
         if (! $msg->is_private) {
@@ -122,9 +123,6 @@ class TelegramController extends Controller
             }
 
             $ticket->reply()->create(['admin_id' => $admin->id, 'content' => $msg->text]);
-            if ($ticket->status !== 1) {
-                $ticket->update(['status' => 1]);
-            }
         }
         (new TelegramService)->sendMessage($msg->chat_id, trans('user.telegram.ticket_reply', ['id' => $ticketId]), 'markdown');
     }
@@ -167,11 +165,10 @@ class TelegramController extends Controller
             return;
         }
         $user = $oauth->user;
-        $transferEnable = formatBytes($user->transfer_enable);
         $up = formatBytes($user->u);
         $down = formatBytes($user->d);
-        $remaining = formatBytes($user->transfer_enable - ($user->u + $user->d));
-        $text = '🚥'.trans('user.subscribe.info.title')."\n———————————————\n".trans('user.subscribe.info.total').": `$transferEnable`\n".trans('user.subscribe.info.upload').": `$up`\n".trans('user.subscribe.info.download').": `$down`\n".trans('user.account.remain').": `$remaining`";
+        $remaining = formatBytes($user->unused_traffic);
+        $text = '🚥'.trans('user.subscribe.info.title')."\n———————————————\n".trans('user.subscribe.info.total').": `$user->transfer_enable_formatted`\n".trans('user.subscribe.info.upload').": `$up`\n".trans('user.subscribe.info.download').": `$down`\n".trans('user.account.remain').": `$remaining`";
         $telegramService->sendMessage($msg->chat_id, $text, 'markdown');
     }
 

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

@@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
 use App\Models\Order;
 use App\Models\ReferralApply;
 use App\Models\ReferralLog;
-use App\Services\UserService;
 use App\Utils\Helpers;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
@@ -26,7 +25,7 @@ class AffiliateController extends Controller
             'referral_money' => Helpers::getPriceTag(sysConfig('referral_money')),
             'totalAmount' => ReferralLog::uid()->sum('commission') / 100,
             'canAmount' => Helpers::getPriceTag(ReferralLog::uid()->whereStatus(0)->sum('commission') / 100),
-            'aff_link' => (new UserService)->inviteURI(),
+            'aff_link' => auth()->user()->invite_url,
             'referralLogList' => ReferralLog::uid()->with('invitee:id,username')->latest()->paginate(10, ['*'], 'log_page'),
             'referralApplyList' => ReferralApply::uid()->latest()->paginate(10, ['*'], 'apply_page'),
             'referralUserList' => auth()->user()->invitees()->select(['username', 'created_at'])->latest()->paginate(10, ['*'], 'user_page'),

+ 3 - 4
app/Http/Controllers/User/ArticleController.php

@@ -13,13 +13,12 @@ class ArticleController extends Controller
 {
     public function index(NodeService $nodeService): View
     { // 帮助中心
-        $subscribe = auth()->user()->subscribe;
+        $user = auth()->user();
 
         return view('user.knowledge', [
             'subType' => $nodeService->getActiveNodeTypes(),
-            'subUrl' => route('sub', $subscribe->code),
-            'subStatus' => $subscribe->status,
-            'subMsg' => $subscribe->ban_desc,
+            'subUrl' => $user->sub_url,
+            'subscribe' => $user->subscribe->only(['status', 'ban_desc']),
             'knowledge' => Article::type(1)->lang()->orderByDesc('sort')->latest()->get()->groupBy('category'),
         ]);
     }

+ 2 - 2
app/Http/Controllers/User/ShopController.php

@@ -34,8 +34,8 @@ class ShopController extends Controller
             $nodes = Node::all();
         }
         foreach ($goodsList as $goods) {
-            $goods->node_count = $nodes->where('level', '<=', $goods->level)->count();
-            $goods->node_countries = $nodes->where('level', '<=', $goods->level)->pluck('country_code')->unique();
+            $goods->node_count = $nodes->where('level', '<=', $goods->level)->where('status', 1)->count();
+            $goods->node_countries = $nodes->where('level', '<=', $goods->level)->where('status', 1)->pluck('country_code')->unique();
         }
 
         return view('user.services', [

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

@@ -52,7 +52,7 @@ class SubscribeController extends Controller
                 $this->failed(trans('errors.subscribe.banned_until', ['time' => $user->ban_time]));
             }
 
-            $unusedTraffic = $user->transfer_enable - $user->used_traffic;
+            $unusedTraffic = $user->unused_traffic;
             if ($unusedTraffic <= 0) {
                 $this->failed(trans('errors.subscribe.out'));
             }

+ 17 - 19
app/Http/Controllers/UserController.php

@@ -21,32 +21,30 @@ class UserController extends Controller
 {
     use DataChart;
 
-    public function index(NodeService $nodeService): View
+    public function index(NodeService $nodeService, UserService $userService): View
     {
-        // 用户转换
-        if (session()->has('user')) {
+        if (session()->has('user')) { // 用户转换
             auth()->loginUsingId(session()->pull('user'));
         }
         $user = auth()->user();
-        $totalTransfer = $user->transfer_enable;
-        $usedTransfer = $user->used_traffic;
-        $unusedTraffic = max($totalTransfer - $usedTransfer, 0);
+
+        $user->load(['subscribe', 'loginLogs' => function ($query) {
+            $query->latest()->first();
+        }]);
 
         return view('user.index', array_merge([
-            'remainDays' => now()->diffInDays($user->expired_at, false),
-            'resetDays' => $user->reset_time ? now()->diffInDays($user->reset_time, false) : null,
-            'unusedTraffic' => $unusedTraffic,
-            'expireTime' => $user->expiration_date,
-            'banedTime' => $user->ban_time,
-            'unusedPercent' => $totalTransfer > 0 ? round($unusedTraffic / $totalTransfer, 2) * 100 : 0,
-            'announcements' => Article::type(2)->lang()->latest()->simplePaginate(1), // 公告
-            'isTrafficWarning' => $user->isTrafficWarning(), // 流量异常判断
-            'paying_user' => (new UserService)->isActivePaying(), // 付费用户判断
-            'userLoginLog' => $user->loginLogs()->latest()->first(), // 近期登录日志
-            'subscribe_status' => $user->subscribe->status,
-            'subMsg' => $user->subscribe->ban_desc,
+            'remainDays' => $userService->getRemainingDays(),
+            'resetDays' => $userService->getResetDays(),
+            'unusedPercent' => $userService->getUnusedTrafficPercent(),
+            'announcements' => cache()->remember('announcements_'.app()->getLocale(), 300, function () {
+                return Article::type(2)->lang()->latest()->simplePaginate(1); // 公告缓存 5 分钟
+            }), // 公告
+            'isTrafficWarning' => $userService->isTrafficWarning(), // 流量异常判断
+            'paying_user' => $userService->isActivePaying(), // 付费用户判断
+            'user' => $user->only(['sub_url', 'unused_traffic', 'expiration_date', 'ban_time']),
+            'userLoginLog' => $user->loginLogs->first(), // 近期登录日志
             'subType' => $nodeService->getActiveNodeTypes($user->nodes()),
-            'subUrl' => $user->subUrl(),
+            'subscribe' => $user->subscribe->only(['status', 'ban_desc']),
         ], $this->dataFlowChart($user->id)));
     }
 

+ 91 - 109
app/Models/User.php

@@ -7,8 +7,8 @@ use App\Casts\money;
 use App\Observers\UserObserver;
 use App\Utils\Avatar;
 use App\Utils\Helpers;
-use DB;
 use Hash;
+use Hashids\Hashids;
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -44,38 +44,6 @@ class User extends Authenticatable
         return $this->username;
     }
 
-    public function usedTrafficPercentage(): float
-    {
-        return round($this->used_traffic / $this->transfer_enable, 2);
-    }
-
-    public function getUsedTrafficAttribute(): int
-    {
-        return $this->d + $this->u;
-    }
-
-    public function getExpirationDateAttribute(): ?string
-    {
-        return $this->attributes['expired_at'];
-    }
-
-    public function getResetDateAttribute(): ?string
-    {
-        return $this->attributes['reset_time'];
-    }
-
-    public function getTelegramUserIdAttribute(): ?string
-    {
-        $telegram = $this->userAuths()->whereType('telegram')->first();
-
-        return $telegram->identifier ?? null;
-    }
-
-    public function userAuths(): HasMany
-    {
-        return $this->hasMany(UserOauth::class);
-    }
-
     public function onlineIpLogs(): HasMany
     {
         return $this->hasMany(NodeOnlineIp::class);
@@ -151,11 +119,6 @@ class User extends Authenticatable
         return $this->hasOne(UserSubscribe::class);
     }
 
-    public function subUrl(): string
-    {
-        return route('sub', $this->subscribe->code);
-    }
-
     public function subscribeLogs(): HasManyThrough
     {
         return $this->hasManyThrough(UserSubscribeLog::class, UserSubscribe::class);
@@ -181,42 +144,19 @@ class User extends Authenticatable
         return $this->hasMany(__CLASS__, 'inviter_id');
     }
 
-    public function getLevelNameAttribute(): string
-    {
-        return Level::whereLevel($this->level)->first()->name;
-    }
-
-    public function getCreditTagAttribute(): string
-    {
-        return Helpers::getPriceTag($this->credit);
-    }
-
-    public function getTransferEnableFormattedAttribute(): string
+    public function userGroup(): BelongsTo
     {
-        return formatBytes($this->attributes['transfer_enable']);
+        return $this->belongsTo(UserGroup::class);
     }
 
-    public function setPasswordAttribute(string $password): string
+    public function orders(): HasMany
     {
-        return $this->attributes['password'] = Hash::make($password);
+        return $this->hasMany(Order::class);
     }
 
-    public function getAvatarAttribute(): string
+    public function paidOrders(): hasMany
     {
-        $img = session('avatar_url_'.$this->id);
-        if ($img) {
-            return $img;
-        }
-        if ($this->qq) {
-            $img = Avatar::getQQAvatar($this->qq);
-        } elseif (stripos(strtolower($this->username), '@qq.com') !== false) {
-            $img = Avatar::getQQAvatar($this->username);
-        } else {
-            $img = Avatar::getRandomAvatar($this->username);
-        }
-        session(['avatar_url_'.$this->id => $img]);
-
-        return $img;
+        return $this->hasMany(Order::class)->where('status', 2)->whereNotNull('goods_id')->where('is_expire', 0)->where('amount', '>', 0);
     }
 
     public function scopeActiveUser(Builder $query): Builder
@@ -229,57 +169,39 @@ class User extends Authenticatable
         return $query->where('status', '<>', -1)->whereEnable(0);
     }
 
-    public function nodes(?int $userLevel = null, ?int $userGroupId = null): Node|Builder|BelongsToMany
-    {
-        if ($userGroupId === null && $this->user_group_id) { // 使用默认的用户分组
-            $query = $this->userGroup->nodes();
-        } elseif ($userGroupId) { // 使用给的用户分组
-            $query = UserGroup::findOrFail($userGroupId)->nodes();
-        } else { // 无用户分组
-            $query = Node::query();
-        }
-
-        return $query->whereStatus(1)->where('level', '<=', $userLevel ?? $this->level ?? 0);
-    }
-
-    public function userGroup(): BelongsTo
+    public function getExpirationDateAttribute(): ?string
     {
-        return $this->belongsTo(UserGroup::class);
+        return $this->attributes['expired_at'];
     }
 
-    public function getIsAvailableAttribute(): bool
+    public function getResetDateAttribute(): ?string
     {
-        return ! $this->ban_time && $this->transfer_enable && $this->expired_at > time();
+        return $this->attributes['reset_time'];
     }
 
-    public function updateCredit(float $credit): bool
+    public function getTelegramUserIdAttribute(): ?string
     {
-        $this->credit += $credit;
+        $telegram = $this->userAuths()->whereType('telegram')->first();
 
-        return $this->credit >= 0 && $this->save();
+        return $telegram->identifier ?? null;
     }
 
-    public function incrementData(int $data): bool
-    { // 添加用户流量
-        $this->transfer_enable += $data;
-
-        return $this->save();
+    public function userAuths(): HasMany
+    {
+        return $this->hasMany(UserOauth::class);
     }
 
-    public function isNotCompleteOrderByUserId(int $userId): bool
+    public function getCreditTagAttribute(): string
     {
-        return Order::uid($userId)->whereIn('status', [0, 1])->exists();
+        return Helpers::getPriceTag($this->credit);
     }
 
-    public function trafficFetch(int $u, int $d): bool
+    public function getTransferEnableFormattedAttribute(): string
     {
-        $this->u += $u;
-        $this->d += $d;
-
-        return $this->save();
+        return formatBytes($this->attributes['transfer_enable']);
     }
 
-    public function expiration_status(): int
+    public function getExpirationStatusAttribute(): int
     {
         $today = date('Y-m-d');
         $nextMonth = date('Y-m-d', strtotime('next month'));
@@ -295,24 +217,84 @@ class User extends Authenticatable
         return $status ?? 3;
     }
 
-    public function isTrafficWarning(): bool
-    { // 流量异常警告
-        return ((int) sysConfig('traffic_ban_value') * GiB) <= $this->recentTrafficUsed();
+    public function getSubUrlAttribute(): string
+    {
+        return route('sub', $this->subscribe->code);
     }
 
-    public function recentTrafficUsed()
+    public function getInviteCodeAttribute(): string
     {
-        return UserHourlyDataFlow::userRecentUsed($this->id)->sum(DB::raw('u + d'));
+        $uid = $this->id;
+        $affSalt = sysConfig('aff_salt');
+
+        return empty($affSalt) ? $uid : (new Hashids($affSalt, 8))->encode($uid);
     }
 
-    public function orders(): HasMany
+    public function getInviteUrlAttribute(): string
     {
-        return $this->hasMany(Order::class);
+        return sysConfig('website_url').route('register', ['aff' => $this->invite_code], false);
     }
 
-    public function paidOrders()
+    public function getUsedTrafficAttribute(): int
     {
-        return $this->hasMany(Order::class)->where('status', 2)->whereNotNull('goods_id')->where('is_expire', 0)->where('amount', '>', 0);
+        return $this->d + $this->u;
+    }
+
+    public function getUnusedTrafficAttribute(): int
+    {
+        return max($this->transfer_enable - $this->d - $this->u, 0);
+    }
+
+    public function getAvatarAttribute(): string
+    {
+        return session()->remember('avatar_url_'.$this->id, function () {
+            if ($this->qq) {
+                return Avatar::getQQAvatar($this->qq);
+            }
+
+            if (str(str($this->username)->lower())->endsWith('@qq.com')) {
+                return Avatar::getQQAvatar($this->username);
+            }
+
+            return Avatar::getRandomAvatar($this->username);
+        });
+    }
+
+    public function getLevelNameAttribute(): string
+    {
+        return Level::where('level', $this->level)->value('name');
+    }
+
+    public function setPasswordAttribute(string $password): string
+    {
+        return $this->attributes['password'] = Hash::make($password);
+    }
+
+    public function nodes(?int $userLevel = null, ?int $userGroupId = null): Node|Builder|BelongsToMany
+    {
+        if ($userGroupId === null && $this->user_group_id) { // 使用默认的用户分组
+            $query = $this->userGroup->nodes();
+        } elseif ($userGroupId) { // 使用给的用户分组
+            $query = UserGroup::findOrFail($userGroupId)->nodes();
+        } else { // 无用户分组
+            $query = Node::query();
+        }
+
+        return $query->whereStatus(1)->where('level', '<=', $userLevel ?? $this->level ?? 0);
+    }
+
+    public function updateCredit(float $credit): bool
+    {
+        $this->credit += $credit;
+
+        return $this->credit >= 0 && $this->save();
+    }
+
+    public function incrementData(int $data): bool
+    { // 添加用户流量
+        $this->transfer_enable += $data;
+
+        return $this->save();
     }
 
     public function routeNotificationForTelegram()

+ 6 - 2
app/Models/UserHourlyDataFlow.php

@@ -28,12 +28,16 @@ class UserHourlyDataFlow extends Model
     }
 
     // 用户每时使用总流量
-    public function scopeUserHourly(Builder $query, int $uid): Builder
+    public function scopeUserHourly(Builder $query, ?int $uid): Builder
     {
+        if (! $uid) {
+            $uid = auth()->id();
+        }
+
         return $query->whereUserId($uid)->whereNodeId(null);
     }
 
-    public function scopeUserRecentUsed(Builder $query, int $uid): Builder
+    public function scopeUserRecentUsed(Builder $query, ?int $uid = null): Builder
     {
         return $query->userHourly($uid)->where('created_at', '>=', date('Y-m-d H:i:s', time() - 3900));
     }

+ 1 - 1
app/Services/ArticleService.php

@@ -12,7 +12,7 @@ class ArticleService
     {
         $siteName = sysConfig('website_name');
         $siteUrl = sysConfig('website_url');
-        $subUrl = auth()->user()?->subUrl();
+        $subUrl = auth()->user()?->sub_url;
 
         self::$valuables = [
             '{{siteName}}' => $siteName,

+ 3 - 17
app/Services/NodeService.php

@@ -9,24 +9,10 @@ class NodeService
 {
     public function getActiveNodeTypes(?Builder $nodes = null): array
     {
-        if (! $nodes) {
-            $nodes = Node::whereStatus(1);
-        }
-        $types = $nodes->pluck('type')->unique();
+        $types = ($nodes ?? Node::whereStatus(1))->pluck('type');
 
-        if ($types->contains(0)) {
-            $data[] = 'ss';
-        }
-        if ($types->contains(1) || $types->contains(4)) {
-            $data[] = 'ssr';
-        }
-        if ($types->contains(2)) {
-            $data[] = 'v2';
-        }
-        if ($types->contains(3)) {
-            $data[] = 'trojan';
-        }
+        $map = [0 => 'ss', 1 => 'ssr', 2 => 'v2', 3 => 'trojan', 4 => 'ssr'];
 
-        return $data ?? [];
+        return $types->intersect(array_keys($map))->map(fn ($type) => $map[$type])->unique()->values()->all();
     }
 }

+ 26 - 15
app/Services/ProxyService.php

@@ -7,22 +7,19 @@ use App\Models\User;
 use App\Utils\Clients\Protocols\Text;
 use App\Utils\Clients\Protocols\URLSchemes;
 use Arr;
+use Exception;
 use ReflectionClass;
+use RuntimeException;
 
 class ProxyService
 {
-    private static ?User $user;
-
     private static array $servers;
 
-    public function __construct(?User $user = null)
-    {
-        $this->setUser($user ?? auth()->user());
-    }
+    private ?User $user;
 
-    public function setUser(?User $user = null): void
+    public function __construct(?User $user = null)
     {
-        self::$user = $user;
+        $this->user = $user ?? auth()->user();
     }
 
     public function getProxyText(string $target, ?int $type = null): string
@@ -55,7 +52,7 @@ class ProxyService
 
     public function getNodeList(?int $type = null, bool $isConfig = true): array
     {
-        $query = self::$user->nodes()->whereIn('is_display', [2, 3]); // 获取这个账号可用节点
+        $query = $this->getUser()->nodes()->whereIn('is_display', [2, 3]); // 获取这个账号可用节点
 
         if ($type) {
             if ($type === 1) {
@@ -79,9 +76,27 @@ class ProxyService
         return $nodes;
     }
 
+    private function getUser(): User
+    {
+        if (! $this->user || ! $this->user->exists) {
+            $user = auth()->user();
+            if (! $user) {
+                throw new RuntimeException('User not authenticated');
+            }
+            $this->setUser($user);
+        }
+
+        return $this->user;
+    }
+
+    public function setUser(User $user): void
+    {
+        $this->user = $user;
+    }
+
     public function getProxyConfig(Node $node): array
     { // 提取节点信息
-        $user = self::$user;
+        $user = $this->getUser();
         $config = [
             'id' => $node->id,
             'name' => $node->name,
@@ -193,11 +208,6 @@ class ProxyService
         return URLSchemes::build($this->getServers()); // Origin
     }
 
-    public function getUser(): ?User
-    {
-        return self::$user;
-    }
-
     public function getProxyCode(string $target, ?int $type = null): ?string
     {// 客户端用代理信息
         $servers = $this->getNodeList($type);
@@ -219,6 +229,7 @@ class ProxyService
             'shadowsocksr' => $type->buildShadowsocksr($server),
             'vmess' => $type->buildVmess($server),
             'trojan' => $type->buildTrojan($server),
+            default => throw new Exception('Unsupported proxy type: '.$server['type']),
         };
     }
 }

+ 49 - 11
app/Services/UserService.php

@@ -3,20 +3,21 @@
 namespace App\Services;
 
 use App\Models\User;
-use Hashids\Hashids;
+use DB;
+use RuntimeException;
 
 class UserService
 {
-    private static User $user;
+    private ?User $user;
 
     public function __construct(?User $user = null)
     {
-        self::$user = $user ?? auth()->user();
+        $this->user = $user ?? auth()->user();
     }
 
     public function getProfile(): array
     {
-        $user = self::$user;
+        $user = $this->getUser();
 
         return [
             'id' => $user->id,
@@ -40,21 +41,58 @@ class UserService
             'reset_time' => $user->reset_date,
             'invite_num' => $user->invite_num,
             'status' => $user->status,
-            'invite_url' => $this->inviteURI(),
+            'invite_url' => $user->invite_url,
         ];
     }
 
-    public function inviteURI(bool $isCode = false): string
+    private function getUser(): User
     {
-        $uid = self::$user->id;
-        $affSalt = sysConfig('aff_salt');
-        $aff = empty($affSalt) ? $uid : (new Hashids($affSalt, 8))->encode($uid);
+        if (! $this->user || ! $this->user->exists) {
+            $user = auth()->user();
+            if (! $user) {
+                throw new RuntimeException('User not authenticated');
+            }
+            $this->setUser($user);
+        }
 
-        return $isCode ? $aff : sysConfig('website_url').route('register', ['aff' => $aff], false);
+        return $this->user;
+    }
+
+    public function setUser(User $user): void
+    {
+        $this->user = $user;
+    }
+
+    public function getRemainingDays(): int
+    {
+        return now()->diffInDays($this->getUser()->expired_at, false);
+    }
+
+    public function getResetDays(): ?int
+    {
+        return $this->getUser()->reset_time ? now()->diffInDays($this->getUser()->reset_time, false) : null;
+    }
+
+    public function getUnusedTrafficPercent(): float
+    {
+        $totalTransfer = $this->getUser()->transfer_enable;
+
+        return $totalTransfer > 0 ? round($this->getUser()->unused_traffic * 100 / $totalTransfer, 2) : 0;
+    }
+
+    public function isTrafficWarning(): bool
+    { // 流量异常警告
+        return ((int) sysConfig('traffic_ban_value') * GiB) <= $this->recentTrafficUsed();
+    }
+
+    public function recentTrafficUsed(): int
+    {
+        return $this->getUser()->hourlyDataFlows()->userRecentUsed()
+            ->sum(DB::raw('u + d')); // 假设 traffic_used 是记录流量消耗的字段
     }
 
     public function isActivePaying(): bool
     {
-        return self::$user->orders()->active()->where('origin_amount', '>', 0)->exists();
+        return $this->getUser()->orders()->active()->where('origin_amount', '>', 0)->exists();
     }
 }

+ 15 - 25
app/Utils/Avatar.php

@@ -6,38 +6,41 @@ use Http;
 use Illuminate\Http\Client\PendingRequest;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
+use Str;
 
 class Avatar
 {
     private static PendingRequest $basicRequest;
 
-    public static function getAvatar(Request $request): JsonResponse
+    public static function get(Request $request): JsonResponse
+    {
+        return response()->json(self::getAvatar($request->input('qq'), $request->input('username')));
+    }
+
+    private static function getAvatar(?string $qq = null, ?string $username = null): string
     {
-        $username = $request->input('username');
-        $qq = $request->input('qq');
         if ($qq) {
-            $url = self::getQQAvatar($qq);
-        } elseif ($username && stripos(strtolower($request->input('username')), '@qq.com') !== false) {
-            $url = self::getQQAvatar($username);
-        } else {
-            $url = self::getRandomAvatar($username);
+            return self::getQQAvatar($qq);
         }
 
-        return response()->json($url);
+        if ($username && Str::endsWith(Str::lower($username), '@qq.com')) {
+            return self::getQQAvatar($username);
+        }
+
+        return self::getRandomAvatar($username);
     }
 
     public static function getQQAvatar(string $qq): ?string
     {
-        self::$basicRequest = Http::timeout(15)->withOptions(['http_errors' => false])->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36')->replaceHeaders(['Referer' => null]);
+        self::$basicRequest = Http::timeout(15)->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
         $ret = null;
         $source = 1;
 
-        while ($source <= 4 && $ret === null) {
+        while ($source <= 3 && $ret === null) {
             $ret = match ($source) {
                 1 => self::qLogo("https://q.qlogo.cn/g?b=qq&nk=$qq&s=100"),
                 2 => self::qZonePortrait("https://users.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=$qq", $qq),
                 3 => self::qLogo("https://thirdqq.qlogo.cn/g?b=qq&nk=$qq&s=100"),
-                4 => self::qqLogin($qq),
             };
             $source++;
         }
@@ -70,19 +73,6 @@ class Avatar
         return null;
     }
 
-    private static function qqLogin(string $qq): ?string
-    {
-        $response = self::$basicRequest->get("https://ptlogin.qq.com/getface?imgtype=3&uin=$qq");
-        if ($response->ok()) {
-            $data = $response->body();
-            if ($data) {
-                return json_decode(substr($data, 13, -1), true)[$qq];
-            }
-        }
-
-        return null;
-    }
-
     public static function getRandomAvatar(string $username): string
     {
         // 'https://api.sretna.cn/kind/ar.php','https://api.qjqq.cn/api/MiYouShe',

+ 1 - 1
app/Utils/Clients/Shadowrocket.php

@@ -16,7 +16,7 @@ class Shadowrocket implements Client
         //display remaining traffic and expire date
         if (sysConfig('is_custom_subscribe')) {
             $usedTraffic = formatBytes($user->used_traffic);
-            $remainTraffic = formatBytes($user->transfer_enable - $user->used_traffic);
+            $remainTraffic = formatBytes($user->unused_traffic);
             $uri = "STATUS=📊:{$usedTraffic}💾:{$remainTraffic}📅:$user->expiration_date\r\n";
         }
 

+ 4 - 4
app/Utils/Clients/Surge.php

@@ -42,13 +42,13 @@ class Surge implements Client
         if (sysConfig('is_custom_subscribe')) {
             $upload = formatBytes($user->u);
             $download = formatBytes($user->d);
-            $totalTraffic = formatBytes($user->transfer_enable);
+            $totalTraffic = $user->transfer_enable_formatted;
             $style = 'info';
-            $remainTraffic = $user->transfer_enable - $user->d - $user->u;
+            $remainTraffic = $user->unused_traffic;
             $remainDates = now()->diffInDays($user->expired_at, false);
             if ($remainTraffic <= 0 || $remainDates <= 0) {
                 $style = 'error';
-            } elseif (($user->transfer_enable - $user->d - $user->u) / $user->transfer_enable <= 0.05 || $remainDates <= 7) {
+            } elseif ($remainTraffic / $user->transfer_enable <= 0.05 || $remainDates <= 7) {
                 $style = 'alert';
             }
 
@@ -57,7 +57,7 @@ class Surge implements Client
             $subscribeInfo = "title=$webName, content=";
         }
 
-        return str_replace(['$subscribe_info', '$subs_link', '$subs_domain', '$proxies', '$proxy_group'], [$subscribeInfo, $user->subUrl(), $_SERVER['HTTP_HOST'], $proxyProfiles['proxies'], $proxyProfiles['names']],
+        return str_replace(['$subscribe_info', '$subs_link', '$subs_domain', '$proxies', '$proxy_group'], [$subscribeInfo, $user->sub_url, $_SERVER['HTTP_HOST'], $proxyProfiles['proxies'], $proxyProfiles['name']],
             $config);
     }
 }

+ 1 - 1
app/Utils/Clients/V2rayN.php

@@ -19,7 +19,7 @@ class V2rayN implements Client
                 if ($user->transfer_enable === 0) {
                     $text .= trans('user.account.remain').': 0';
                 } else {
-                    $text .= trans('user.account.remain').': '.formatBytes($user->transfer_enable);
+                    $text .= trans('user.account.remain').': '.$user->transfer_enable_formatted;
                 }
                 $text .= ', '.trans('model.user.expired_date').": $user->expiration_date";
             } else {

+ 1 - 1
app/Utils/NetworkDetection.php

@@ -41,7 +41,7 @@ class NetworkDetection
     private function networkCheck(string $ip, int $port): ?array
     { // 通过众多API进行节点阻断检测.
         $checkers = ['toolsdaquan', 'vps234', 'flyzy2005', 'idcoffer', 'ip112', 'upx8', 'rss', 'gd', 'vps1352', 'akile'];
-        self::$basicRequest = Http::timeout(15)->retry(2)->withOptions(['http_errors' => false])->withoutVerifying()->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
+        self::$basicRequest = Http::timeout(15)->retry(2)->withOptions(['http_errors' => false])->withoutVerifying()->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36');
 
         foreach ($checkers as $checker) {
             try {

+ 2 - 2
resources/views/admin/ticket/reply.blade.php

@@ -96,8 +96,8 @@
                             </dd>
                             <dt class="col-sm-3">{{ trans('model.user.expired_date') }}</dt>
                             <dd class="col-sm-9">
-                                @if ($user->expiration_status() !== 3)
-                                    <span class="badge badge-lg badge-{{ ['danger', 'warning', 'default'][$user->expiration_status()] }}">
+                                @if ($user->expiration_status !== 3)
+                                    <span class="badge badge-lg badge-{{ ['danger', 'warning', 'default'][$user->expiration_status] }}">
                                         {{ $user->expiration_date }} </span>
                                 @else
                                     {{ $user->expiration_date }}

+ 4 - 4
resources/views/admin/user/index.blade.php

@@ -106,7 +106,7 @@
                     </thead>
                     <tbody>
                         @foreach ($userList as $user)
-                            <tr class="{{ $user->isTrafficWarning() ? 'table-danger' : '' }}">
+                            <tr class="{{ $user->ban_time ? 'table-danger' : '' }}">
                                 <td> {{ $user->id }} </td>
                                 <td> {{ $user->username }} </td>
                                 <td> {{ $user->credit }} </td>
@@ -114,14 +114,14 @@
                                     {!! $user->port ?: '<span class="badge badge-lg badge-danger"> ' . trans('common.none') . ' </span>' !!}
                                 </td>
                                 <td>
-                                    <a class="copySubscribeLink" data-clipboard-action="copy" data-clipboard-text="{{ $user->subUrl() }}"
+                                    <a class="copySubscribeLink" data-clipboard-action="copy" data-clipboard-text="{{ $user->sub_url }}"
                                        href="javascript:">{{ $user->subscribe->code }}</a>
                                 </td>
                                 <td> {{ formatBytes($user->used_traffic) }} / {{ $user->transfer_enable_formatted }} </td>
                                 <td> {{ $user->t ? date('Y-m-d H:i', $user->t) : trans('common.status.unused') }} </td>
                                 <td>
-                                    @if ($user->expiration_status() !== 3)
-                                        <span class="badge badge-lg badge-{{ ['danger', 'warning', 'default'][$user->expiration_status()] }}">
+                                    @if ($user->expiration_status !== 3)
+                                        <span class="badge badge-lg badge-{{ ['danger', 'warning', 'default'][$user->expiration_status] }}">
                                             {{ $user->expiration_date }} </span>
                                     @else
                                         {{ $user->expiration_date }}

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

@@ -76,7 +76,7 @@
                                 </li>
                                 <li class="list-group-item">
                                     {{ trans('model.user.traffic_used') }}: {{ formatBytes($user->used_traffic) }}
-                                    / {{ formatBytes($user->transfer_enable) }}
+                                    / {{ $user->transfer_enable_formatted }}
                                 </li>
                                 <li class="list-group-item">
                                     {{ trans('model.user.credit') }}: {{ $user->credit }}

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

@@ -35,7 +35,7 @@
                                     <code>{{ trans('user.attribute.node') }}</code>
                                 </p>
                                 <a class="btn btn-block btn-danger" href="{{ route('shop.index') }}">{{ trans('user.purchase.promotion') }}</a>
-                            @elseif(Auth::user()->enable)
+                            @elseif(auth()->user()->enable)
                                 <i class="wb-check green-400 font-size-40 mr-10" aria-hidden="true"></i>
                                 <span class="font-size-40 font-weight-100">{{ trans('common.status.normal') }}</span>
                                 <p class="font-weight-300 m-0 green-500">{{ trans('user.account.reason.normal') }}</p>
@@ -43,11 +43,11 @@
                                 <i class="wb-close red-400 font-size-40 mr-10" aria-hidden="true"></i>
                                 <span class="font-size-40 font-weight-100">{{ trans('common.status.expire') }}</span>
                                 <p class="font-weight-300 m-0 red-500">{{ trans('user.account.reason.expired') }}</p>
-                            @elseif($unusedTraffic === 0)
+                            @elseif($user['unused_traffic'] === 0)
                                 <i class="wb-close red-400 font-size-40 mr-10"></i>
                                 <span class="font-size-40 font-weight-100">{{ trans('common.status.disabled') }}</span>
                                 <p class="font-weight-300 m-0 red-500">{{ trans('user.account.reason.traffic_exhausted') }}</p>
-                            @elseif($banedTime || Auth::user()->isTrafficWarning())
+                            @elseif($user['ban_time'])
                                 <i class="wb-alert orange-400 font-size-40 mr-10"></i>
                                 <span class="font-size-40 font-weight-100">{{ trans('common.status.limited') }}</span>
                                 <p class="font-weight-300 m-0 orange-500">{!! trans('user.account.reason.overused', ['data' => sysConfig('traffic_ban_value')]) !!}</p>
@@ -68,10 +68,10 @@
                                 </button>
                                 <span class="font-weight-400">{{ trans('user.account.remain') }}</span>
                                 <div class="text-center font-weight-100 font-size-40">
-                                    @if ($unusedTraffic === 0)
+                                    @if ($user['unused_traffic'] === 0)
                                         {{ trans('common.status.run_out') }}
                                     @else
-                                        {{ formatBytes($unusedTraffic) }}
+                                        {{ formatBytes($user['unused_traffic']) }}
                                     @endif
                                     <br />
                                     <h4>{{ trans('user.account.level') }}:
@@ -102,7 +102,7 @@
                         <div class="content-text text-center mb-0">
                             @if ($remainDays >= 0)
                                 <span class="font-size-40 font-weight-100">{{ $remainDays . ' ' . trans_choice('common.days.attribute', 1) }}</span>
-                                <p class="blue-grey-500 font-weight-300 m-0">{{ $expireTime }}</p>
+                                <p class="blue-grey-500 font-weight-300 m-0">{{ $user['expiration_date'] }}</p>
                             @else
                                 <span class="font-size-40 font-weight-100">{{ trans('common.status.expire') }}</span>
                                 <br />
@@ -146,7 +146,7 @@
                                 <h3 class="card-header-transparent">
                                     <i class="icon wb-link-intact"></i>{{ trans('user.subscribe.link') }}
                                 </h3>
-                                @if ($subscribe_status)
+                                @if ($subscribe['status'])
                                     <div class="card-body">
                                         @if (count($subType) > 1)
                                             <div class="form-group row">
@@ -196,7 +196,7 @@
                                             {{ trans('common.copy.attribute') }}</button>
                                     </div>
                                 @else
-                                    <x-alert type="danger" :message="__($subMsg)" />
+                                    <x-alert type="danger" :message="__($subscribe['ban_desc'])" />
                                 @endif
                             </div>
                         </div>
@@ -352,7 +352,7 @@
 
         const clipboard = new ClipboardJS('.mt-clipboard', {
             text: function(trigger) {
-                let base = @json($subUrl);
+                let base = @json($user['sub_url']);
                 const client = $('#client').val();
                 const subType = $('#subType').val();
                 if (subType && client) {
@@ -465,8 +465,8 @@
             options: common_options(@json(trans_choice('common.days.attribute', 2))),
         });
 
-        @if ($banedTime) // 封禁倒计时
-            const banedTime = new Date("{{ $banedTime }}").getTime();
+        @if ($user['ban_time']) // 封禁倒计时
+            const banedTime = new Date("{{ $user['ban_time'] }}").getTime();
             countDown(banedTime, 'banedTime', true);
             setInterval(function() {
                 countDown(banedTime, 'banedTime', true);

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

@@ -46,7 +46,7 @@
                                                     </div>
                                                     <div class="panel-collapse collapse show" id="answer_1" role="tabpanel" aria-labelledby="question_1">
                                                         <div class="panel-body">
-                                                            @if ($subStatus)
+                                                            @if ($subscribe['status'])
                                                                 <x-alert type="warning" :message="trans('user.subscribe.tips')" />
                                                                 <div class="input-group">
                                                                     <input class="form-control" id="sub_link" type="text" value="{{ $subUrl }}" />
@@ -87,7 +87,7 @@
                                                                     </div>
                                                                 </div>
                                                             @else
-                                                                <x-alert type="danger" :message="__($subMsg)" />
+                                                                <x-alert type="danger" :message="__($subscribe['ban_desc'])" />
                                                             @endif
                                                         </div>
                                                     </div>