GeekQuerxy 4 éve
szülő
commit
28782cde94

+ 1 - 1
resources/views/material/user/node.tpl

@@ -15,7 +15,7 @@
     <p>节点地址:<span class="card-tag tag-blue">{$v2server['add']}</span></p>
     <p>节点端口:<span class="card-tag tag-volcano">{$v2server['port']}</span></p>
     <p>AlterId:<span class="card-tag tag-purple">{$v2server['aid']}</span></p>
-    <p>UUID:<span class="card-tag tag-geekblue">{$user->getUuid()}</span></p>
+    <p>UUID:<span class="card-tag tag-geekblue">{$user->uuid}</span></p>
     <p>传输协议:<span class="card-tag tag-green">{$v2server['net']}</span></p>
     {if $v2server['net'] == 'ws'}
         <p>伪装路径:<span class="card-tag tag-green">{$v2server['path']}</span></p>

+ 1 - 42
src/Command/FinanceMail.php

@@ -53,30 +53,7 @@ class FinanceMail extends Command
             ++$income_count;
             $income_total += $code['number'];
         }
-        //易付通的单独表
-        $datatables2 = new Datatables(new DatatablesHelper());
-        $datatables2->query('select COUNT(*) as "count_yft" from INFORMATION_SCHEMA.TABLES where TABLE_NAME = "yft_order_info"');
-        $count_yft = $datatables2->generate();
-        if (strpos($count_yft, '"count_yft":1')) {
-            $datatables2->query(
-                'select yft_order_info.price, yft_order_info.user_id, yft_order_info.create_time from yft_order_info
-				where TO_DAYS(NOW()) - TO_DAYS(yft_order_info.create_time) = 1 and yft_order_info.state= 1'
-            );
-            $text_json2 = $datatables2->generate();
-            $text_array2 = json_decode($text_json2, true);
-            $codes2 = $text_array2['data'];
-            foreach ($codes2 as $code2) {
-                $text_html .= '<tr>';
-                $text_html .= '<td>' . $code2['price'] . '</td>';
-                $text_html .= '<td>' . $code2['user_id'] . '</td>';
-                $user = User::find($code2['user_id']);
-                $text_html .= '<td>' . $user->user_name . '</td>';
-                $text_html .= '<td>' . $code2['create_time'] . '</td>';
-                $text_html .= '</tr>';
-                ++$income_count;
-                $income_total += $code['price'];
-            }
-        }
+
         $text_html .= '</table>';
         $text_html .= '<br>昨日总收入笔数:' . $income_count . '<br>昨日总收入金额:' . $income_total;
 
@@ -122,24 +99,6 @@ class FinanceMail extends Command
             ++$income_count;
             $income_total += $code['number'];
         }
-        //易付通的单独表
-        $datatables2 = new Datatables(new DatatablesHelper());
-        $datatables2->query('select COUNT(*) as "count_yft" from INFORMATION_SCHEMA.TABLES where TABLE_NAME = "yft_order_info"');
-        $count_yft = $datatables2->generate();
-        if (strpos($count_yft, '"count_yft":1')) {
-            $datatables2->query(
-                'select yft_order_info.price from yft_order_info
-				where yearweek(date_format(yft_order_info.create_time,\'%Y-%m-%d\')) = yearweek(now())-1 and yft_order_info.state= 1'
-            );
-            //每周的第一天是周日,因此统计周日~周六的七天
-            $text_json2 = $datatables2->generate();
-            $text_array2 = json_decode($text_json2, true);
-            $codes2 = $text_array2['data'];
-            foreach ($codes2 as $code2) {
-                ++$income_count;
-                $income_total += $code2['price'];
-            }
-        }
 
         $text_html .= '<br>上周总收入笔数:' . $income_count . '<br>上周总收入金额:' . $income_total;
 

+ 1 - 17
src/Command/Job.php

@@ -112,7 +112,6 @@ class Job extends Command
 
         // ------- 重置自增 ID
         $db = new DatatablesHelper();
-        Tools::reset_auto_increment($db, 'user_traffic_log');
         Tools::reset_auto_increment($db, 'ss_node_online_log');
         Tools::reset_auto_increment($db, 'ss_node_info');
         // ------- 重置自增 ID
@@ -183,22 +182,7 @@ class Job extends Command
         });
 
         // ------- 更新 IP 库
-        $qqwry = file_get_contents('https://qqwry.mirror.noc.one/QQWry.Dat?from=sspanel_uim');
-        if ($qqwry != '') {
-            rename(BASE_PATH . '/storage/qqwry.dat', BASE_PATH . '/storage/qqwry.dat.bak');
-            $fp = fopen(BASE_PATH . '/storage/qqwry.dat', 'wb');
-            if ($fp) {
-                fwrite($fp, $qqwry);
-                fclose($fp);
-            }
-            $iplocation   = new QQWry();
-            $location     = $iplocation->getlocation('8.8.8.8');
-            $Userlocation = $location['country'];
-            if (iconv('gbk', 'utf-8//IGNORE', $Userlocation) !== '美国') {
-                unlink(BASE_PATH . '/storage/qqwry.dat');
-                rename(BASE_PATH . '/storage/qqwry.dat.bak', BASE_PATH . '/storage/qqwry.dat');
-            }
-        }
+        (new Tool($this->argv))->initQQWry();
         // ------- 更新 IP 库
 
         // ------- 发送每日系统运行报告

+ 17 - 2
src/Command/Tool.php

@@ -2,6 +2,8 @@
 
 namespace App\Command;
 
+use App\Utils\QQWry;
+
 class Tool extends Command
 {
     public $description = ''
@@ -71,14 +73,27 @@ class Tool extends Command
      */
     public function initQQWry()
     {
-        echo ('开始下载纯真 IP 数据库....');
+        echo ('开始下载或更新纯真 IP 数据库....');
+        $path  = BASE_PATH . '/storage/qqwry.dat';
         $qqwry = file_get_contents('https://qqwry.mirror.noc.one/QQWry.Dat?from=sspanel_uim');
         if ($qqwry != '') {
-            $fp = fopen(BASE_PATH . '/storage/qqwry.dat', 'wb');
+            if (is_file($path)) {
+                rename($path, $path . '.bak');
+            }
+            $fp = fopen($path, 'wb');
             if ($fp) {
                 fwrite($fp, $qqwry);
                 fclose($fp);
                 echo ('纯真 IP 数据库下载成功!');
+                $iplocation   = new QQWry();
+                $location     = $iplocation->getlocation('8.8.8.8');
+                $Userlocation = $location['country'];
+                if (iconv('gbk', 'utf-8//IGNORE', $Userlocation) !== '美国') {
+                    unlink($path);
+                    if (is_file($path . '.bak')) {
+                        rename($path . '.bak', $path);
+                    }
+                }
             } else {
                 echo ('纯真 IP 数据库保存失败!');
             }

+ 3 - 6
src/Command/User.php

@@ -102,11 +102,8 @@ class User extends Command
         $users = ModelsUser::all();
         $current_timestamp = time();
         foreach ($users as $user) {
-            $user->uuid = Uuid::uuid3(
-                Uuid::NAMESPACE_DNS,
-                $user->email . '|' . $current_timestamp
-            );
-            $user->save();
+            /** @var ModelsUser $user */
+            $user->generateUUID($current_timestamp);
         }
         echo 'generate UUID successful' . PHP_EOL;
     }
@@ -118,7 +115,7 @@ class User extends Command
      */
     public function generateGa()
     {
-        $users = User::all();
+        $users = ModelsUser::all();
         foreach ($users as $user) {
             $ga = new GA();
             $secret = $ga->createSecret();

+ 1 - 1
src/Controllers/LinkController.php

@@ -575,7 +575,7 @@ class LinkController extends BaseController
             'port'            => 10086,
             'method'          => 'chacha20-ietf',
             'passwd'          => $user->passwd,
-            'id'              => $user->getUuid(),
+            'id'              => $user->uuid,
             'aid'             => 0,
             'net'             => 'tcp',
             'headerType'      => 'none',

+ 15 - 53
src/Controllers/UserController.php

@@ -4,7 +4,6 @@ namespace App\Controllers;
 
 use App\Services\{
     Auth,
-    Mail,
     Config,
     Payment
 };
@@ -18,7 +17,6 @@ use App\Models\{
     Token,
     Bought,
     Coupon,
-    Ticket,
     Payback,
     BlockIp,
     LoginIp,
@@ -32,7 +30,6 @@ use App\Models\{
 };
 use App\Utils\{
     GA,
-    Pay,
     URL,
     Hash,
     Check,
@@ -42,12 +39,14 @@ use App\Utils\{
     Geetest,
     Telegram,
     ClientProfiles,
-    DatatablesHelper,
     TelegramSessionManager
 };
 use voku\helper\AntiXSS;
-use Exception;
 use Ramsey\Uuid\Uuid;
+use Slim\Http\{
+    Request,
+    Response
+};
 
 /**
  *  HomeController
@@ -802,6 +801,7 @@ class UserController extends BaseController
 
     public function buy($request, $response, $args)
     {
+        $user   = $this->user;
         $coupon = $request->getParam('coupon');
         $coupon = trim($coupon);
         $code = $coupon;
@@ -867,7 +867,6 @@ class UserController extends BaseController
         }
 
         $price = $shop->price * ((100 - $credit) / 100);
-        $user = $this->user;
 
         if (!$user->isLogin) {
             $res['ret'] = -1;
@@ -1159,55 +1158,18 @@ class UserController extends BaseController
         return $this->echoJson($response, $res);
     }
 
+    /**
+     * @param Request   $request
+     * @param Response  $response
+     * @param array     $args
+     */
     public function updateMethod($request, $response, $args)
     {
-        $user = Auth::getUser();
-        $method = $request->getParam('method');
-        $method = strtolower($method);
-
-        if ($method == '') {
-            $res['ret'] = 0;
-            $res['msg'] = '非法输入';
-            return $response->getBody()->write(json_encode($res));
-        }
-
-        if (!Tools::is_param_validate('method', $method)) {
-            $res['ret'] = 0;
-            $res['msg'] = '加密无效';
-            return $response->getBody()->write(json_encode($res));
-        }
-
-        $user->method = $method;
-
-        if (!Tools::checkNoneProtocol($user)) {
-            $res['ret'] = 0;
-            $res['msg'] = '系统检测到您将要设置的加密方式为 none ,但您的协议并不在以下协议<br>' . implode(',', Config::getSupportParam('allow_none_protocol')) . '<br>之内,请您先修改您的协议,再来修改此处设置。';
-            return $this->echoJson($response, $res);
-        }
-
-        if (!URL::SSCanConnect($user) && !URL::SSRCanConnect($user)) {
-            $res['ret'] = 0;
-            $res['msg'] = '您这样设置之后,就没有客户端能连接上了,所以系统拒绝了您的设置,请您检查您的设置之后再进行操作。';
-            return $this->echoJson($response, $res);
-        }
-
-        $user->updateMethod($method);
-
-        if (!URL::SSCanConnect($user)) {
-            $res['ret'] = 1;
-            $res['msg'] = '设置成功,但您目前的协议,混淆,加密方式设置会导致 Shadowsocks原版客户端无法连接,请您自行更换到 ShadowsocksR 客户端。';
-            return $this->echoJson($response, $res);
-        }
-
-        if (!URL::SSRCanConnect($user)) {
-            $res['ret'] = 1;
-            $res['msg'] = '设置成功,但您目前的协议,混淆,加密方式设置会导致 ShadowsocksR 客户端无法连接,请您自行更换到 Shadowsocks 客户端。';
-            return $this->echoJson($response, $res);
-        }
-
-        $res['ret'] = 1;
-        $res['msg'] = '设置成功,您可自由选用两种客户端来进行连接。';
-        return $this->echoJson($response, $res);
+        $user          = $this->user;
+        $method        = strtolower($request->getParam('method'));
+        $result        = $user->updateMethod($method);
+        $result['ret'] = $result['ok'] ? 1 : 0;
+        return $response->withJson($result);
     }
 
     public function logout($request, $response, $args)

+ 0 - 10
src/Models/CheckInLog.php

@@ -1,10 +0,0 @@
-<?php
-
-
-namespace App\Models;
-
-class CheckInLog
-{
-    protected $connection = 'default';
-    protected $table = 'ss_checkin_log';
-}

+ 2 - 2
src/Models/Node.php

@@ -273,7 +273,7 @@ class Node extends Model
         $item           = Tools::v2Array($this->server);
         $item['type']   = 'vmess';
         $item['remark'] = ($emoji ? Tools::addEmoji($this->name) : $this->name);
-        $item['id']     = $user->getUuid();
+        $item['id']     = $user->uuid;
         $item['class']  = $this->node_class;
         return $item;
     }
@@ -341,7 +341,7 @@ class Node extends Model
         $item['type']     = 'trojan';
         $item['address']  = $server[0];
         $item['port']     = (isset($opt['port']) ? (int) $opt['port'] : 443);
-        $item['passwd']   = $user->getUuid();
+        $item['passwd']   = $user->uuid;
         $item['host']     = $item['address'];
         if (isset($opt['host'])) {
             $item['host'] = $opt['host'];

+ 0 - 49
src/Models/Relay.php

@@ -1,49 +0,0 @@
-<?php
-
-namespace App\Models;
-
-/**
- * Relay Model
- */
-class Relay extends Model
-{
-    protected $connection = 'default';
-    protected $table = 'relay';
-
-    public function User()
-    {
-        $user = User::where('id', $this->attributes['user_id'])->first();
-        if ($user == null) {
-            self::where('id', '=', $this->attributes['id'])->delete();
-            return null;
-        }
-
-        return $user;
-    }
-
-    public function Source_Node()
-    {
-        $node = Node::where('id', $this->attributes['source_node_id'])->first();
-        if ($node == null && $this->attributes['source_node_id'] != 0) {
-            self::where('id', '=', $this->attributes['id'])->delete();
-            return null;
-        }
-
-        return $node;
-    }
-
-    public function Dist_Node()
-    {
-        if ($this->attributes['dist_node_id'] == -1) {
-            return null;
-        }
-
-        $node = Node::where('id', $this->attributes['dist_node_id'])->first();
-        if ($node == null) {
-            self::where('id', '=', $this->attributes['id'])->delete();
-            return null;
-        }
-
-        return $node;
-    }
-}

+ 0 - 12
src/Models/Role.php

@@ -1,12 +0,0 @@
-<?php
-
-namespace App\Models;
-
-/**
- * Node Model
- */
-class Role extends Model
-{
-    protected $connection = 'default';
-    protected $table = 'user_role';
-}

+ 248 - 303
src/Models/User.php

@@ -6,7 +6,6 @@ use App\Utils\{
     Tools,
     Hash,
     GA,
-    QQWry,
     Telegram,
     URL
 };
@@ -29,10 +28,18 @@ class User extends Model
 
     protected $table = 'user';
 
+    /**
+     * 已登录
+     *
+     * @var bool
+     */
     public $isLogin;
 
-    public $isAdmin;
-
+    /**
+     * 强制类型转换
+     *
+     * @var array
+     */
     protected $casts = [
         't'               => 'float',
         'u'               => 'float',
@@ -46,37 +53,35 @@ class User extends Model
         'sendDailyMail'   => 'int'
     ];
 
-    public function getGravatarAttribute()
-    {
-        $hash = md5(strtolower(trim($this->attributes['email'])));
-        return 'https://cdn.v2ex.com/gravatar/' . $hash . "?&d=identicon";
-    }
-
-    public function isAdmin()
+    /**
+     * Gravatar 头像地址
+     */
+    public function getGravatarAttribute(): string
     {
-        return $this->attributes['is_admin'];
+        $hash = md5(strtolower(trim($this->email)));
+        return 'https://cdn.v2ex.com/gravatar/' . $hash . '?&d=identicon';
     }
 
-    public function lastSsTime()
+    /**
+     * 是管理员
+     */
+    public function isAdmin(): bool
     {
-        if ($this->attributes['t'] == 0) {
-            return '从未使用喵';
-        }
-        return Tools::toDateTime($this->attributes['t']);
+        return $this->is_admin;
     }
 
     public function getMuMd5()
     {
         $str = str_replace(
             array('%id', '%suffix'),
-            array($this->attributes['id'], $_ENV['mu_suffix']),
+            array($this->id, $_ENV['mu_suffix']),
             $_ENV['mu_regex']
         );
         preg_match_all("|%-?[1-9]\d*m|U", $str, $matches, PREG_PATTERN_ORDER);
         foreach ($matches[0] as $key) {
             $key_match = str_replace(array('%', 'm'), '', $key);
             $md5 = substr(
-                MD5($this->attributes['id'] . $this->attributes['passwd'] . $this->attributes['method'] . $this->attributes['obfs'] . $this->attributes['protocol']),
+                MD5($this->id . $this->passwd . $this->method . $this->obfs . $this->protocol),
                 ($key_match < 0 ? $key_match : 0),
                 abs($key_match)
             );
@@ -85,183 +90,237 @@ class User extends Model
         return $str;
     }
 
-    public function lastCheckInTime()
+    /**
+     * 最后使用时间
+     */
+    public function lastSsTime(): string
     {
-        if ($this->attributes['last_check_in_time'] == 0) {
-            return '从未签到';
-        }
-        return Tools::toDateTime($this->attributes['last_check_in_time']);
+        return $this->t == 0 ? '从未使用喵' : Tools::toDateTime($this->t);
+    }
+
+    /**
+     * 最后签到时间
+     */
+    public function lastCheckInTime(): string
+    {
+        return $this->last_check_in_time == 0 ? '从未签到' : Tools::toDateTime($this->last_check_in_time);
     }
 
-    public function regDate()
+    /**
+     * 注册时间
+     */
+    public function regDate(): string
     {
-        return $this->attributes['reg_date'];
+        return $this->reg_date;
     }
 
-    public function updatePassword($pwd)
+    /**
+     * 更新密码
+     *
+     * @param string $pwd
+     */
+    public function updatePassword(string $pwd): bool
     {
         $this->pass = Hash::passwordHash($pwd);
-        $this->save();
+        return $this->save();
     }
 
     public function get_forbidden_ip()
     {
-        return str_replace(',', PHP_EOL, $this->attributes['forbidden_ip']);
+        return str_replace(',', PHP_EOL, $this->forbidden_ip);
     }
 
     public function get_forbidden_port()
     {
-        return str_replace(',', PHP_EOL, $this->attributes['forbidden_port']);
+        return str_replace(',', PHP_EOL, $this->forbidden_port);
     }
 
-    public function updateSsPwd($pwd)
+    /**
+     * 更新连接密码
+     *
+     * @param string $pwd
+     */
+    public function updateSsPwd(string $pwd): bool
     {
         $this->passwd = $pwd;
-        $this->save();
+        return $this->save();
     }
 
-    public function updateMethod($method)
+    /**
+     * 更新加密方式
+     *
+     * @param string $method
+     */
+    public function updateMethod(string $method): array
     {
+        $return = [
+            'ok' => false
+        ];
+        if ($method == '') {
+            $return['msg'] = '非法输入';
+            return $return;
+        }
+        if (!Tools::is_param_validate('method', $method)) {
+            $return['msg'] = '加密无效';
+            return $return;
+        }
         $this->method = $method;
+        if (!Tools::checkNoneProtocol($this)) {
+            $return['msg'] = '系统检测到您将要设置的加密方式为 none ,但您的协议并不在以下协议【' . implode(',', Config::getSupportParam('allow_none_protocol')) . '】之内,请您先修改您的协议,再来修改此处设置。';
+            return $return;
+        }
+        if (!URL::SSCanConnect($this) && !URL::SSRCanConnect($this)) {
+            $return['msg'] = '您这样设置之后,就没有客户端能连接上了,所以系统拒绝了您的设置,请您检查您的设置之后再进行操作。';
+            return $return;
+        }
         $this->save();
+        $return['ok'] = true;
+        if (!URL::SSCanConnect($this)) {
+            $return['msg']  = '设置成功,但您目前的协议,混淆,加密方式设置会导致 Shadowsocks 原版客户端无法连接,请您自行更换到 ShadowsocksR 客户端。';
+        }
+        if (!URL::SSRCanConnect($this)) {
+            $return['msg']  = '设置成功,但您目前的协议,混淆,加密方式设置会导致 ShadowsocksR 客户端无法连接,请您自行更换到 Shadowsocks 客户端。';
+        }
+        $return['msg'] = '设置成功,您可自由选用两种客户端来进行连接。';
+        return $return;
     }
 
-    public function addInviteCode()
+    /**
+     * 添加邀请码
+     */
+    public function addInviteCode(): string
     {
-        $uid = $this->attributes['id'];
-        $code = new InviteCode();
         while (true) {
             $temp_code = Tools::genRandomChar(4);
-            if (InviteCode::where('user_id', $uid)->count() == 0) {
-                break;
+            if (InviteCode::where('code', $temp_code)->first() == null) {
+                if (InviteCode::where('user_id', $this->id)->count() == 0) {
+                    $code          = new InviteCode();
+                    $code->code    = $temp_code;
+                    $code->user_id = $this->id;
+                    $code->save();
+                    return $temp_code;
+                } else {
+                    return (InviteCode::where('user_id', $this->id)->first())->code;
+                }
             }
         }
-        $code->code = $temp_code;
-        $code->user_id = $uid;
-        $code->save();
     }
 
-    public function getUuid()
+    /**
+     * 生成新的UUID
+     */
+    public function generateUUID($s): bool
     {
-        $uuid = $this->attributes['uuid'];
-        return $uuid;
+        $this->uuid = Uuid::uuid3(
+            Uuid::NAMESPACE_DNS,
+            $this->email . '|' . $s
+        );
+        return $this->save();
     }
 
     /*
-     * 总流量
+     * 总流量[自动单位]
      */
-    public function enableTraffic()
+    public function enableTraffic(): string
     {
-        $transfer_enable = $this->attributes['transfer_enable'];
-        return Tools::flowAutoShow($transfer_enable);
+        return Tools::flowAutoShow($this->transfer_enable);
     }
 
     /*
-     * 总流量[GB]
+     * 总流量[GB],不含单位
      */
-    public function enableTrafficInGB()
+    public function enableTrafficInGB(): float
     {
-        $transfer_enable = $this->attributes['transfer_enable'];
-        return Tools::flowToGB($transfer_enable);
+        return Tools::flowToGB($this->transfer_enable);
     }
 
     /*
-     * 已用流量
+     * 已用流量[自动单位]
      */
-    public function usedTraffic()
+    public function usedTraffic(): string
     {
-        $total = $this->attributes['u'] + $this->attributes['d'];
-        return Tools::flowAutoShow($total);
+        return Tools::flowAutoShow($this->u + $this->d);
     }
 
     /*
      * 已用流量占总流量的百分比
      */
-    public function trafficUsagePercent()
+    public function trafficUsagePercent(): int
     {
-        $total = $this->attributes['u'] + $this->attributes['d'];
-        $transferEnable = $this->attributes['transfer_enable'];
-        if ($transferEnable == 0) {
+        if ($this->transfer_enable == 0) {
             return 0;
         }
-        $percent = $total / $transferEnable;
-        $percent = round($percent, 2);
+        $percent  = ($this->u + $this->d) / $this->transfer_enable;
+        $percent  = round($percent, 2);
         $percent *= 100;
         return $percent;
     }
 
     /*
-     * 剩余流量
+     * 剩余流量[自动单位]
      */
-    public function unusedTraffic()
+    public function unusedTraffic(): string
     {
-        $total = $this->attributes['u'] + $this->attributes['d'];
-        $transfer_enable = $this->attributes['transfer_enable'];
-        return Tools::flowAutoShow($transfer_enable - $total);
+        return Tools::flowAutoShow($this->transfer_enable - ($this->u + $this->d));
     }
 
     /*
      * 剩余流量占总流量的百分比
      */
-    public function unusedTrafficPercent()
+    public function unusedTrafficPercent(): int
     {
-        $transferEnable = $this->attributes['transfer_enable'];
-        if ($transferEnable == 0) {
+        if ($this->transfer_enable == 0) {
             return 0;
         }
-        $unusedTraffic = $transferEnable - ($this->attributes['u'] + $this->attributes['d']);
-        $percent = $unusedTraffic / $transferEnable;
-        $percent = round($percent, 2);
+        $unused   = $this->transfer_enable - ($this->u + $this->d);
+        $percent  = $unused / $this->transfer_enable;
+        $percent  = round($percent, 2);
         $percent *= 100;
         return $percent;
     }
 
     /*
-     * 今天使用的流量
+     * 今天使用的流量[自动单位]
      */
-    public function TodayusedTraffic()
+    public function TodayusedTraffic(): string
     {
-        $total = $this->attributes['u'] + $this->attributes['d'] - $this->attributes['last_day_t'];
-        return Tools::flowAutoShow($total);
+        return Tools::flowAutoShow($this->u + $this->d - $this->last_day_t);
     }
 
     /*
      * 今天使用的流量占总流量的百分比
      */
-    public function TodayusedTrafficPercent()
+    public function TodayusedTrafficPercent(): int
     {
-        $transferEnable = $this->attributes['transfer_enable'];
-        if ($transferEnable == 0) {
+        if ($this->transfer_enable == 0) {
             return 0;
         }
-        $TodayusedTraffic = $this->attributes['u'] + $this->attributes['d'] - $this->attributes['last_day_t'];
-        $percent = $TodayusedTraffic / $transferEnable;
-        $percent = round($percent, 2);
-        $percent *= 100;
+        $Todayused = $this->u + $this->d - $this->last_day_t;
+        $percent   = $Todayused / $this->transfer_enable;
+        $percent   = round($percent, 2);
+        $percent  *= 100;
         return $percent;
     }
 
     /*
-     * 今天之前已使用的流量
+     * 今天之前已使用的流量[自动单位]
      */
-    public function LastusedTraffic()
+    public function LastusedTraffic(): string
     {
-        $total = $this->attributes['last_day_t'];
-        return Tools::flowAutoShow($total);
+        return Tools::flowAutoShow($this->last_day_t);
     }
 
     /*
      * 今天之前已使用的流量占总流量的百分比
      */
-    public function LastusedTrafficPercent()
+    public function LastusedTrafficPercent(): int
     {
-        $transferEnable = $this->attributes['transfer_enable'];
-        if ($transferEnable == 0) {
+        if ($this->transfer_enable == 0) {
             return 0;
         }
-        $LastusedTraffic = $this->attributes['last_day_t'];
-        $percent = $LastusedTraffic / $transferEnable;
-        $percent = round($percent, 2);
+        $Lastused = $this->last_day_t;
+        $percent  = $Lastused / $this->transfer_enable;
+        $percent  = round($percent, 2);
         $percent *= 100;
         return $percent;
     }
@@ -269,197 +328,114 @@ class User extends Model
     /*
      * 是否可以签到
      */
-    public function isAbleToCheckin()
-    {
-        $last = $this->attributes['last_check_in_time'];
-
-        $now = time();
-        return date('Ymd', $now) != date('Ymd', $last);
-    }
-
-    /*
-     * @param traffic 单位 MB
-     */
-    public function addTraffic($traffic)
+    public function isAbleToCheckin(): bool
     {
+        return date('Ymd') != date('Ymd', $this->last_check_in_time);
     }
 
     public function getGAurl()
     {
         $ga = new GA();
         $url = $ga->getUrl(
-            urlencode($_ENV['appName'] . '-' . $this->attributes['user_name'] . '-两步验证码'),
-            $this->attributes['ga_token']
+            urlencode($_ENV['appName'] . '-' . $this->user_name . '-两步验证码'),
+            $this->ga_token
         );
         return $url;
     }
 
-    public function inviteCodes()
+    /**
+     * 获取用户的邀请码
+     */
+    public function getInviteCodes(): ?InviteCode
     {
-        $uid = $this->attributes['id'];
-        return InviteCode::where('user_id', $uid)->get();
+        return InviteCode::where('user_id', $this->id)->first();
     }
 
-    public function ref_by_user()
+    /**
+     * 用户的邀请人
+     */
+    public function ref_by_user(): ?User
     {
-        $uid = $this->attributes['ref_by'];
-        return self::where('id', $uid)->first();
+        return self::where('id', $this->ref_by)->first();
     }
 
+    /**
+     * 删除用户的订阅链接
+     */
     public function clean_link()
     {
-        $uid = $this->attributes['id'];
-        Link::where('userid', $uid)->delete();
+        Link::where('userid', $this->id)->delete();
     }
 
+    /**
+     * 删除用户的邀请码
+     */
     public function clear_inviteCodes()
     {
-        $uid = $this->attributes['id'];
-        InviteCode::where('user_id', $uid)->delete();
+        InviteCode::where('user_id', $this->id)->delete();
     }
 
-    public function online_ip_count()
+    /**
+     * 在线 IP 个数
+     */
+    public function online_ip_count(): int
     {
-        $uid = $this->attributes['id'];
-        $total = Ip::where('datetime', '>=', time() - 90)->where('userid', $uid)->orderBy('userid', 'desc')->get();
-        $unique_ip_list = array();
+        // 根据 IP 分组去重
+        $total = Ip::where('datetime', '>=', time() - 90)->where('userid', $this->id)->orderBy('userid', 'desc')->groupBy('ip')->get();
+        $ip_list = [];
         foreach ($total as $single_record) {
-            $single_record->ip = Tools::getRealIp($single_record->ip);
-            $is_node = Node::where('node_ip', $single_record->ip)->first();
-            if ($is_node) {
+            $ip = Tools::getRealIp($single_record->ip);
+            if (Node::where('node_ip', $ip)->first() != null) {
                 continue;
             }
-
-            if (!in_array($single_record->ip, $unique_ip_list)) {
-                $unique_ip_list[] = $single_record->ip;
-            }
+            $ip_list[] = $ip;
         }
-
-        return count($unique_ip_list);
+        return count($ip_list);
     }
 
-    public function kill_user()
+    /**
+     * 销户
+     */
+    public function kill_user(): bool
     {
-        $uid = $this->attributes['id'];
-        $email = $this->attributes['email'];
+        $uid   = $this->id;
+        $email = $this->email;
 
         Bought::where('userid', '=', $uid)->delete();
-        Ip::where('userid', '=', $uid)->delete();
         Code::where('userid', '=', $uid)->delete();
+        DetectBanLog::where('user_id', '=', $uid)->delete();
         DetectLog::where('user_id', '=', $uid)->delete();
+        EmailVerify::where('email', $email)->delete();
+        InviteCode::where('user_id', '=', $uid)->delete();
+        Ip::where('userid', '=', $uid)->delete();
         Link::where('userid', '=', $uid)->delete();
         LoginIp::where('userid', '=', $uid)->delete();
-        InviteCode::where('user_id', '=', $uid)->delete();
+        PasswordReset::where('email', '=', $email)->delete();
         TelegramSession::where('user_id', '=', $uid)->delete();
-        UnblockIp::where('userid', '=', $uid)->delete();
         Token::where('user_id', '=', $uid)->delete();
-        PasswordReset::where('email', '=', $email)->delete();
+        UnblockIp::where('userid', '=', $uid)->delete();
         UserSubscribeLog::where('user_id', '=', $uid)->delete();
-        DetectBanLog::where('user_id', '=', $uid)->delete();
 
         $this->delete();
 
         return true;
     }
 
-    public function get_table_json_array()
-    {
-        $id = $this->attributes['id'];
-        $today_traffic = Tools::flowToMB($this->attributes['u'] + $this->attributes['d'] - $this->attributes['last_day_t']);
-        $is_enable = $this->attributes['enable'] == 1 ? '可用' : '禁用';
-        $reg_location = $this->attributes['reg_ip'];
-        $account_expire_in = $this->attributes['expire_in'];
-        $class_expire_in = $this->attributes['class_expire'];
-        $used_traffic = Tools::flowToGB($this->attributes['u'] + $this->attributes['d']);
-        $enable_traffic = Tools::flowToGB($this->attributes['transfer_enable']);
-
-        $im_type = '';
-        $im_value = $this->attributes['im_value'];
-        switch ($this->attributes['im_type']) {
-            case 1:
-                $im_type = '微信';
-                break;
-            case 2:
-                $im_type = 'QQ';
-                break;
-            case 3:
-                $im_type = 'Google+';
-                break;
-            default:
-                $im_type = 'Telegram';
-                $im_value = '<a href="https://telegram.me/' . $im_value . '">' . $im_value . '</a>';
-        }
-
-        $ref_user = self::find($this->attributes['ref_by']);
-
-        if ($this->attributes['ref_by'] == 0) {
-            $ref_user_id = 0;
-            $ref_user_name = '系统邀请';
-        } elseif ($ref_user == null) {
-            $ref_user_id = $this->attributes['ref_by'];
-            $ref_user_name = '邀请人已经被删除';
-        } else {
-            $ref_user_id = $this->attributes['ref_by'];
-            $ref_user_name = $ref_user->user_name;
-        }
-
-        $iplocation = new QQWry();
-        $location = $iplocation->getlocation($reg_location);
-        $reg_location .= "\n" . iconv('gbk', 'utf-8//IGNORE', $location['country'] . $location['area']);
-
-        $return_array = array(
-            'DT_RowId' => 'row_1_' . $id,
-            $id,
-            $id,
-            $this->attributes['user_name'],
-            $this->attributes['remark'],
-            $this->attributes['email'],
-            $this->attributes['money'],
-            $im_type,
-            $im_value,
-            $this->attributes['node_group'],
-            $account_expire_in,
-            $this->attributes['class'],
-            $class_expire_in,
-            $this->attributes['passwd'],
-            $this->attributes['port'],
-            $this->attributes['method'],
-            $this->attributes['protocol'],
-            $this->attributes['obfs'],
-            $this->attributes['obfs_param'],
-            $this->online_ip_count(),
-            $this->lastSsTime(),
-            $used_traffic,
-            $enable_traffic,
-            $this->lastCheckInTime(),
-            $today_traffic,
-            $is_enable,
-            $this->attributes['reg_date'],
-            $reg_location,
-            $this->attributes['auto_reset_day'],
-            $this->attributes['auto_reset_bandwidth'],
-            $ref_user_id,
-            $ref_user_name
-        );
-        return $return_array;
-    }
-
-    public function get_user_attributes($key)
-    {
-        return $this->attributes[$key];
-    }
-
-    public function get_top_up()
+    /**
+     * 累计充值金额
+     */
+    public function get_top_up(): float
     {
-        $codes = Code::where('userid', $this->attributes['id'])->get();
-        $top_up = 0;
-        foreach ($codes as $code) {
-            $top_up += $code->number;
-        }
-        return round($top_up, 2);
+        $number = Code::where('userid', $this->id)->sum('number');
+        return is_null($number) ? 0.00 : round($number, 2);
     }
 
-    public function calIncome($req)
+    /**
+     * 获取累计收入
+     *
+     * @param string $req
+     */
+    public function calIncome(string $req): float
     {
         switch ($req) {
             case "yesterday":
@@ -469,43 +445,54 @@ class User extends Model
                 $number = Code::whereDate('usedatetime', '=', date('Y-m-d'))->sum('number');
                 break;
             case "this month":
-                $number = Code::whereYear('usedatetime','=',date('Y'))->whereMonth('usedatetime', '=', date('m'))->sum('number');
+                $number = Code::whereYear('usedatetime', '=', date('Y'))->whereMonth('usedatetime', '=', date('m'))->sum('number');
                 break;
             case "last month":
-                $number = Code::whereYear('usedatetime','=',date('Y'))->whereMonth('usedatetime', '=', date('m', strtotime('last month')))->sum('number');
+                $number = Code::whereYear('usedatetime', '=', date('Y'))->whereMonth('usedatetime', '=', date('m', strtotime('last month')))->sum('number');
                 break;
             default:
                 $number = Code::sum('number');
                 break;
         }
-        return is_null($number) ? 0 : $number;
+        return is_null($number) ? 0.00 : round($number, 2);
     }
 
-    public function paidUserCount()
+    /**
+     * 获取付费用户总数
+     */
+    public function paidUserCount(): int
     {
         return self::where('class', '!=', '0')->count();
     }
 
-    public function disableReason()
+    /**
+     * 获取用户被封禁的理由
+     */
+    public function disableReason(): string
     {
-        $reason_id = DetectLog::where('user_id', '=', $this->attributes['id'])->orderBy('id', 'DESC')->first();
-        $reason = DetectRule::where('id', '=', $reason_id->list_id)->get();
-        return $reason[0]->text;
+        $reason_id = DetectLog::where('user_id', $this->id)->orderBy('id', 'DESC')->first();
+        $reason    = DetectRule::find($reason_id->list_id);
+        if (is_null($reason)) {
+            return '特殊原因被禁用,了解详情请联系管理员';
+        }
+        return $reason->text;
     }
 
-    // 最后一次被封禁的时间
+    /**
+     * 最后一次被封禁的时间
+     */
     public function last_detect_ban_time(): string
     {
-        return ($this->attributes['last_detect_ban_time'] == '1989-06-04 00:05:00'
-            ? '未被封禁过'
-            : $this->attributes['last_detect_ban_time']);
+        return ($this->last_detect_ban_time == '1989-06-04 00:05:00' ? '未被封禁过' : $this->last_detect_ban_time);
     }
 
-    // 当前解封时间
+    /**
+     * 当前解封时间
+     */
     public function relieve_time(): string
     {
-        $logs = DetectBanLog::where('user_id', $this->attributes['id'])->orderBy('id', 'desc')->first();
-        if ($this->attributes['enable'] == 0 && $logs != null) {
+        $logs = DetectBanLog::where('user_id', $this->id)->orderBy('id', 'desc')->first();
+        if ($this->enable == 0 && $logs != null) {
             $time = ($logs->end_time + $logs->ban_time * 60);
             return date('Y-m-d H:i:s', $time);
         } else {
@@ -513,17 +500,20 @@ class User extends Model
         }
     }
 
-    // 累计被封禁的次数
+    /**
+     * 累计被封禁的次数
+     */
     public function detect_ban_number(): int
     {
-        $logs = DetectBanLog::where('user_id', $this->attributes['id'])->get();
-        return count($logs);
+        return DetectBanLog::where('user_id', $this->id)->count();
     }
 
-    // 最后一次封禁的违规次数
+    /**
+     * 最后一次封禁的违规次数
+     */
     public function user_detect_ban_number(): int
     {
-        $logs = DetectBanLog::where('user_id', $this->attributes['id'])->orderBy("id", "desc")->first();
+        $logs = DetectBanLog::where('user_id', $this->id)->orderBy('id', 'desc')->first();
         return $logs->detect_number;
     }
 
@@ -550,50 +540,6 @@ class User extends Model
         return $return;
     }
 
-    /**
-     * 更新加密方式
-     *
-     * @param string $method
-     */
-    public function setMethod($method): array
-    {
-        $return = [
-            'ok'  => true,
-            'msg' => '设置成功,您可自由选用两种客户端来进行连接。'
-        ];
-        if ($method == '') {
-            $return['ok']   = false;
-            $return['msg']  = '非法输入';
-            return $return;
-        }
-        if (!Tools::is_param_validate('method', $method)) {
-            $return['ok']   = false;
-            $return['msg']  = '加密无效';
-            return $return;
-        }
-        $this->method = $method;
-        if (!Tools::checkNoneProtocol($this)) {
-            $return['ok']   = false;
-            $return['msg']  = '系统检测到您将要设置的加密方式为 none ,但您的协议并不在以下协议【' . implode(',', Config::getSupportParam('allow_none_protocol')) . '】之内,请您先修改您的协议,再来修改此处设置。';
-            return $return;
-        }
-        if (!URL::SSCanConnect($this) && !URL::SSRCanConnect($this)) {
-            $return['ok']   = false;
-            $return['msg']  = '您这样设置之后,就没有客户端能连接上了,所以系统拒绝了您的设置,请您检查您的设置之后再进行操作。';
-            return $return;
-        }
-        $this->updateMethod($method);
-        if (!URL::SSCanConnect($this)) {
-            $return['ok']   = true;
-            $return['msg']  = '设置成功,但您目前的协议,混淆,加密方式设置会导致 Shadowsocks 原版客户端无法连接,请您自行更换到 ShadowsocksR 客户端。';
-        }
-        if (!URL::SSRCanConnect($this)) {
-            $return['ok']   = true;
-            $return['msg']  = '设置成功,但您目前的协议,混淆,加密方式设置会导致 ShadowsocksR 客户端无法连接,请您自行更换到 Shadowsocks 客户端。';
-        }
-        return $return;
-    }
-
     /**
      * 更新协议
      *
@@ -730,8 +676,7 @@ class User extends Model
                 'msg' => '端口已被占用'
             ];
         }
-        $origin_port    = $this->port;
-        $this->port     = $Port;
+        $this->port = $Port;
         $this->save();
         return [
             'ok'  => true,
@@ -846,13 +791,13 @@ class User extends Model
      * @param array  $ary
      * @param array  $files
      */
-    public function sendMail(string $subject, string $template, array $ary = [], array $files = [],$is_queue = false): bool
+    public function sendMail(string $subject, string $template, array $ary = [], array $files = [], $is_queue = false): bool
     {
         $result = false;
-        if($is_queue){
+        if ($is_queue) {
             $new_emailqueue = new EmailQueue;
             $new_emailqueue->to_email = $this->email;
-            $new_emailqueue -> subject = $subject;
+            $new_emailqueue->subject = $subject;
             $new_emailqueue->template = $template;
             $new_emailqueue->time = time();
             $new_emailqueue->array = json_encode($ary);
@@ -914,7 +859,7 @@ class User extends Model
                 return;
             case 1:
                 echo 'Send daily mail to user: ' . $this->id;
-                    $this->sendMail(
+                $this->sendMail(
                     $_ENV['appName'] . '-每日流量报告以及公告',
                     'news/daily-traffic-report.tpl',
                     [

+ 1 - 1
src/Utils/URL.php

@@ -407,7 +407,7 @@ class URL
         $item['type'] = 'vmess';
         $item['ps'] = ($emoji ? Tools::addEmoji($node->name) : $node->name);
         $item['remark'] = $item['ps'];
-        $item['id'] = $user->getUuid();
+        $item['id'] = $user->uuid;
         $item['class'] = $node->node_class;
         if (!$arrout) {
             return 'vmess://' . base64_encode(