浏览代码

feat: inactive user detection

M1Screw 2 年之前
父节点
当前提交
ba3010e275

+ 0 - 1
app/routes.php

@@ -169,7 +169,6 @@ return static function (Slim\App $app): void {
         $group->get('/user', App\Controllers\Admin\UserController::class . ':index');
         $group->get('/user', App\Controllers\Admin\UserController::class . ':index');
         $group->get('/user/{id}/edit', App\Controllers\Admin\UserController::class . ':edit');
         $group->get('/user/{id}/edit', App\Controllers\Admin\UserController::class . ':edit');
         $group->put('/user/{id}', App\Controllers\Admin\UserController::class . ':update');
         $group->put('/user/{id}', App\Controllers\Admin\UserController::class . ':update');
-        $group->post('/user/changetouser', App\Controllers\Admin\UserController::class . ':changetouser');
         $group->post('/user/create', App\Controllers\Admin\UserController::class . ':createNewUser');
         $group->post('/user/create', App\Controllers\Admin\UserController::class . ':createNewUser');
         $group->delete('/user/{id}', App\Controllers\Admin\UserController::class . ':delete');
         $group->delete('/user/{id}', App\Controllers\Admin\UserController::class . ':delete');
         $group->post('/user/ajax', App\Controllers\Admin\UserController::class . ':ajax');
         $group->post('/user/ajax', App\Controllers\Admin\UserController::class . ':ajax');

+ 40 - 0
config/settings.json

@@ -1469,6 +1469,46 @@
         "default": "0",
         "default": "0",
         "mark": "是否启用审计封禁"
         "mark": "是否启用审计封禁"
     },
     },
+    {
+        "id": null,
+        "item": "enable_detect_inactive_user",
+        "value": "0",
+        "class": "cron",
+        "is_public": 0,
+        "type": "bool",
+        "default": "0",
+        "mark": "是否启用闲置用户检测"
+    },
+    {
+        "id": null,
+        "item": "detect_inactive_user_checkin_days",
+        "value": "90",
+        "class": "cron",
+        "is_public": 0,
+        "type": "int",
+        "default": "90",
+        "mark": "未签到时长(天)"
+    },
+    {
+        "id": null,
+        "item": "detect_inactive_user_login_days",
+        "value": "90",
+        "class": "cron",
+        "is_public": 0,
+        "type": "int",
+        "default": "90",
+        "mark": "未登录时长(天)"
+    },
+    {
+        "id": null,
+        "item": "detect_inactive_user_use_days",
+        "value": "90",
+        "class": "cron",
+        "is_public": 0,
+        "type": "int",
+        "default": "90",
+        "mark": "未使用时长(天)"
+    },
     {
     {
         "id": null,
         "id": null,
         "item": "daily_job_hour",
         "item": "daily_job_hour",

+ 6 - 3
db/migrations/2023020100-init.php

@@ -280,7 +280,6 @@ return new class() implements MigrationInterface {
                 `pass` varchar(255) NOT NULL COMMENT '登录密码',
                 `pass` varchar(255) NOT NULL COMMENT '登录密码',
                 `passwd` varchar(255) NOT NULL COMMENT '节点密码',
                 `passwd` varchar(255) NOT NULL COMMENT '节点密码',
                 `uuid` char(36) NOT NULL COMMENT 'UUID',
                 `uuid` char(36) NOT NULL COMMENT 'UUID',
-                `t` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '最后使用时间',
                 `u` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '账户当前上传流量',
                 `u` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '账户当前上传流量',
                 `d` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '账户当前下载流量',
                 `d` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '账户当前下载流量',
                 `transfer_today` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '账户今日所用流量',
                 `transfer_today` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '账户今日所用流量',
@@ -289,7 +288,9 @@ return new class() implements MigrationInterface {
                 `port` smallint(6) unsigned NOT NULL COMMENT '端口',
                 `port` smallint(6) unsigned NOT NULL COMMENT '端口',
                 `last_detect_ban_time` datetime NOT NULL DEFAULT '1989-06-04 00:05:00' COMMENT '最后一次被封禁的时间',
                 `last_detect_ban_time` datetime NOT NULL DEFAULT '1989-06-04 00:05:00' COMMENT '最后一次被封禁的时间',
                 `all_detect_number` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '累计违规次数',
                 `all_detect_number` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '累计违规次数',
+                `last_use_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '最后使用时间',
                 `last_check_in_time` int(11) unsigned DEFAULT 0 COMMENT '最后签到时间',
                 `last_check_in_time` int(11) unsigned DEFAULT 0 COMMENT '最后签到时间',
+                `last_login_time` int(11) unsigned DEFAULT 0 COMMENT '最后登录时间',
                 `reg_date` datetime NOT NULL DEFAULT '1989-06-04 00:05:00' COMMENT '注册时间',
                 `reg_date` datetime NOT NULL DEFAULT '1989-06-04 00:05:00' COMMENT '注册时间',
                 `invite_num` int(11) NOT NULL DEFAULT 0 COMMENT '可用邀请次数',
                 `invite_num` int(11) NOT NULL DEFAULT 0 COMMENT '可用邀请次数',
                 `money` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
                 `money` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
@@ -321,7 +322,8 @@ return new class() implements MigrationInterface {
                 `auto_reset_bandwidth` decimal(12,2) unsigned NOT NULL DEFAULT 0 COMMENT '自动重置流量',
                 `auto_reset_bandwidth` decimal(12,2) unsigned NOT NULL DEFAULT 0 COMMENT '自动重置流量',
                 `api_token` char(36) NOT NULL DEFAULT '' COMMENT 'API 密钥',
                 `api_token` char(36) NOT NULL DEFAULT '' COMMENT 'API 密钥',
                 `use_new_shop` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用新商店',
                 `use_new_shop` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用新商店',
-                `is_dark_mode` tinyint(1) NOT NULL DEFAULT 0,
+                `is_dark_mode` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否启用暗黑模式',
+                `is_inactive` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否处于闲置状态',
                 `locale` varchar(16) NOT NULL DEFAULT 'zh-TW' COMMENT '显示语言',
                 `locale` varchar(16) NOT NULL DEFAULT 'zh-TW' COMMENT '显示语言',
                 PRIMARY KEY (`id`),
                 PRIMARY KEY (`id`),
                 UNIQUE KEY `uuid` (`uuid`),
                 UNIQUE KEY `uuid` (`uuid`),
@@ -329,7 +331,8 @@ return new class() implements MigrationInterface {
                 UNIQUE KEY `ga_token` (`ga_token`),
                 UNIQUE KEY `ga_token` (`ga_token`),
                 UNIQUE KEY `api_token` (`api_token`),
                 UNIQUE KEY `api_token` (`api_token`),
                 KEY `is_admin` (`is_admin`),
                 KEY `is_admin` (`is_admin`),
-                KEY `is_banned` (`is_banned`)
+                KEY `is_banned` (`is_banned`),
+                KEY `is_inactive` (`is_inactive`)
             ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
             ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 
             CREATE TABLE `user_coupon` (
             CREATE TABLE `user_coupon` (

+ 34 - 0
db/migrations/2023063000-add_user_is_inactive.php

@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+use App\Interfaces\MigrationInterface;
+use App\Services\DB;
+
+return new class() implements MigrationInterface {
+    public function up(): int
+    {
+        DB::getPdo()->exec("
+            ALTER TABLE user DROP COLUMN IF EXISTS `t`;
+            ALTER TABLE user ADD COLUMN IF NOT EXISTS `is_inactive` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否处于闲置状态';
+            ALTER TABLE user ADD COLUMN IF NOT EXISTS `last_use_time` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '最后使用时间';
+            ALTER TABLE user ADD COLUMN IF NOT EXISTS `last_login_time` int(11) unsigned DEFAULT 0 COMMENT '最后登录时间';
+            ALTER TABLE user ADD KEY IF NOT EXISTS `is_inactive` (`is_inactive`);
+        ");
+
+        return 2023063000;
+    }
+
+    public function down(): int
+    {
+        DB::getPdo()->exec("
+            ALTER TABLE user ADD COLUMN IF NOT EXISTS `t` bigint(20) unsigned NOT NULL DEFAULT 0 COMMENT '最后使用时间';
+            ALTER TABLE user DROP COLUMN IF EXISTS `is_inactive`;
+            ALTER TABLE user DROP COLUMN IF EXISTS `last_use_time`;
+            ALTER TABLE user DROP COLUMN IF EXISTS `last_login_time`;
+            ALTER TABLE user DROP KEY IF EXISTS `is_inactive`;
+        ");
+
+        return 2023061800;
+    }
+};

+ 13 - 13
resources/views/tabler/admin/header.tpl

@@ -96,34 +96,34 @@
                                                 </a>
                                                 </a>
                                                 <div class="dropdown-menu">
                                                 <div class="dropdown-menu">
                                                     <a href="/admin/setting/billing" class="dropdown-item">
                                                     <a href="/admin/setting/billing" class="dropdown-item">
-                                                      财务
+                                                        财务
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/email" class="dropdown-item">
                                                     <a href="/admin/setting/email" class="dropdown-item">
-                                                      邮件
+                                                        邮件
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/support" class="dropdown-item">
                                                     <a href="/admin/setting/support" class="dropdown-item">
-                                                      客服
+                                                        客服
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/captcha" class="dropdown-item">
                                                     <a href="/admin/setting/captcha" class="dropdown-item">
-                                                      验证
+                                                        验证
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/reg" class="dropdown-item">
                                                     <a href="/admin/setting/reg" class="dropdown-item">
-                                                      注册
+                                                        注册
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/ref" class="dropdown-item">
                                                     <a href="/admin/setting/ref" class="dropdown-item">
-                                                      邀请
+                                                        邀请
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/im" class="dropdown-item">
                                                     <a href="/admin/setting/im" class="dropdown-item">
-                                                      IM
+                                                        IM
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/sub" class="dropdown-item">
                                                     <a href="/admin/setting/sub" class="dropdown-item">
-                                                      订阅
-                                                    </a>
-                                                    <a href="/admin/setting/feature" class="dropdown-item">
-                                                      功能
+                                                        订阅
                                                     </a>
                                                     </a>
                                                     <a href="/admin/setting/cron" class="dropdown-item">
                                                     <a href="/admin/setting/cron" class="dropdown-item">
-                                                      定时任务
+                                                        定时任务
+                                                    </a>
+                                                    <a href="/admin/setting/feature" class="dropdown-item">
+                                                        其他设置
                                                     </a>
                                                     </a>
                                                 </div>
                                                 </div>
                                             </div>
                                             </div>
@@ -277,4 +277,4 @@
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>
-</header>
+</header>

+ 50 - 7
resources/views/tabler/admin/setting/cron.tpl

@@ -37,7 +37,10 @@
                             <a href="#finance_mail" class="nav-link" data-bs-toggle="tab">财务报告</a>
                             <a href="#finance_mail" class="nav-link" data-bs-toggle="tab">财务报告</a>
                         </li>
                         </li>
                         <li class="nav-item">
                         <li class="nav-item">
-                            <a href="#detect" class="nav-link" data-bs-toggle="tab">检测任务</a>
+                            <a href="#detect" class="nav-link" data-bs-toggle="tab">审计任务</a>
+                        </li>
+                        <li class="nav-item">
+                            <a href="#inactive" class="nav-link" data-bs-toggle="tab">闲置账号检测</a>
                         </li>
                         </li>
                     </ul>
                     </ul>
                 </div>
                 </div>
@@ -48,13 +51,15 @@
                                 <div class="form-group mb-3 row">
                                 <div class="form-group mb-3 row">
                                     <label class="form-label col-3 col-form-label">每日任务执行时间(小时)</label>
                                     <label class="form-label col-3 col-form-label">每日任务执行时间(小时)</label>
                                     <div class="col">
                                     <div class="col">
-                                        <input id="daily_job_hour" type="text" class="form-control" value="{$settings['daily_job_hour']}">
+                                        <input id="daily_job_hour" type="text" class="form-control"
+                                               value="{$settings['daily_job_hour']}">
                                     </div>
                                     </div>
                                 </div>
                                 </div>
                                 <div class="form-group mb-3 row">
                                 <div class="form-group mb-3 row">
                                     <label class="form-label col-3 col-form-label">每日任务执行时间(分钟)</label>
                                     <label class="form-label col-3 col-form-label">每日任务执行时间(分钟)</label>
                                     <div class="col">
                                     <div class="col">
-                                        <input id="daily_job_minute" type="text" class="form-control" value="{$settings['daily_job_minute']}">
+                                        <input id="daily_job_minute" type="text" class="form-control"
+                                               value="{$settings['daily_job_minute']}">
                                     </div>
                                     </div>
                                 </div>
                                 </div>
                             </div>
                             </div>
@@ -64,7 +69,8 @@
                                 <div class="form-group mb-3 row">
                                 <div class="form-group mb-3 row">
                                     <label class="form-label col-3 col-form-label">是否启用每日财务报告</label>
                                     <label class="form-label col-3 col-form-label">是否启用每日财务报告</label>
                                     <div class="col">
                                     <div class="col">
-                                        <select id="enable_daily_finance_mail" class="col form-select" value="{$settings['enable_daily_finance_mail']}">
+                                        <select id="enable_daily_finance_mail" class="col form-select"
+                                                value="{$settings['enable_daily_finance_mail']}">
                                             <option value="0" {if $settings['enable_daily_finance_mail'] === false}selected{/if}>关闭</option>
                                             <option value="0" {if $settings['enable_daily_finance_mail'] === false}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_daily_finance_mail']}selected{/if}>开启</option>
                                             <option value="1" {if $settings['enable_daily_finance_mail']}selected{/if}>开启</option>
                                         </select>
                                         </select>
@@ -73,7 +79,8 @@
                                 <div class="form-group mb-3 row">
                                 <div class="form-group mb-3 row">
                                     <label class="form-label col-3 col-form-label">是否启用每周财务报告</label>
                                     <label class="form-label col-3 col-form-label">是否启用每周财务报告</label>
                                     <div class="col">
                                     <div class="col">
-                                        <select id="enable_weekly_finance_mail" class="col form-select" value="{$settings['enable_weekly_finance_mail']}">
+                                        <select id="enable_weekly_finance_mail" class="col form-select"
+                                                value="{$settings['enable_weekly_finance_mail']}">
                                             <option value="0" {if $settings['enable_weekly_finance_mail'] === false}selected{/if}>关闭</option>
                                             <option value="0" {if $settings['enable_weekly_finance_mail'] === false}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_weekly_finance_mail']}selected{/if}>开启</option>
                                             <option value="1" {if $settings['enable_weekly_finance_mail']}selected{/if}>开启</option>
                                         </select>
                                         </select>
@@ -82,7 +89,8 @@
                                 <div class="form-group mb-3 row">
                                 <div class="form-group mb-3 row">
                                     <label class="form-label col-3 col-form-label">是否启用每月财务报告</label>
                                     <label class="form-label col-3 col-form-label">是否启用每月财务报告</label>
                                     <div class="col">
                                     <div class="col">
-                                        <select id="enable_monthly_finance_mail" class="col form-select" value="{$settings['enable_monthly_finance_mail']}">
+                                        <select id="enable_monthly_finance_mail" class="col form-select"
+                                                value="{$settings['enable_monthly_finance_mail']}">
                                             <option value="0" {if $settings['enable_monthly_finance_mail'] === false}selected{/if}>关闭</option>
                                             <option value="0" {if $settings['enable_monthly_finance_mail'] === false}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_monthly_finance_mail']}selected{/if}>开启</option>
                                             <option value="1" {if $settings['enable_monthly_finance_mail']}selected{/if}>开启</option>
                                         </select>
                                         </select>
@@ -112,6 +120,41 @@
                                 </div>
                                 </div>
                             </div>
                             </div>
                         </div>
                         </div>
+                        <div class="tab-pane show" id="inactive">
+                            <div class="card-body">
+                                <div class="form-group mb-3 row">
+                                    <label class="form-label col-3 col-form-label">是否启用闲置账号检测</label>
+                                    <div class="col">
+                                        <select id="enable_detect_inactive_user" class="col form-select"
+                                                value="{$settings['enable_detect_inactive_user']}">
+                                            <option value="0" {if $settings['enable_detect_inactive_user'] === false}selected{/if}>关闭</option>
+                                            <option value="1" {if $settings['enable_detect_inactive_user']}selected{/if}>开启</option>
+                                        </select>
+                                    </div>
+                                </div>
+                                <div class="form-group mb-3 row">
+                                    <label class="form-label col-3 col-form-label">未签到时长(天)</label>
+                                    <div class="col">
+                                        <input id="detect_inactive_user_checkin_days" type="text" class="form-control"
+                                               value="{$settings['detect_inactive_user_checkin_days']}">
+                                    </div>
+                                </div>
+                                <div class="form-group mb-3 row">
+                                    <label class="form-label col-3 col-form-label">未登录时长(天)</label>
+                                    <div class="col">
+                                        <input id="detect_inactive_user_login_days" type="text" class="form-control"
+                                               value="{$settings['detect_inactive_user_login_days']}">
+                                    </div>
+                                </div>
+                                <div class="form-group mb-3 row">
+                                    <label class="form-label col-3 col-form-label">未使用时长(天)</label>
+                                    <div class="col">
+                                        <input id="detect_inactive_user_use_days" type="text" class="form-control"
+                                               value="{$settings['detect_inactive_user_use_days']}">
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>
@@ -143,4 +186,4 @@
     });
     });
 </script>
 </script>
 
 
-{include file='admin/footer.tpl'}
+{include file='admin/footer.tpl'}

+ 3 - 3
resources/views/tabler/admin/setting/feature.tpl

@@ -6,10 +6,10 @@
             <div class="row align-items-center">
             <div class="row align-items-center">
                 <div class="col">
                 <div class="col">
                     <h2 class="page-title">
                     <h2 class="page-title">
-                        <span class="home-title">功能设置</span>
+                        <span class="home-title">其他设置</span>
                     </h2>
                     </h2>
                     <div class="page-pretitle my-3">
                     <div class="page-pretitle my-3">
-                        <span class="home-subtitle">设置站点的功能开关与显示</span>
+                        <span class="home-subtitle">设置站点的其他设置</span>
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="col-auto ms-auto d-print-none">
                 <div class="col-auto ms-auto d-print-none">
@@ -99,4 +99,4 @@
     });
     });
 </script>
 </script>
 
 
-{include file='admin/footer.tpl'}
+{include file='admin/footer.tpl'}

+ 4 - 0
src/Command/Cron.php

@@ -64,6 +64,10 @@ EOL;
             $jobs->resetFreeUserTraffic();
             $jobs->resetFreeUserTraffic();
             $jobs->sendDailyTrafficReport();
             $jobs->sendDailyTrafficReport();
 
 
+            if (Setting::obtain('enable_detect_inactive_user')) {
+                $jobs->detectInactiveUser();
+            }
+
             if (Setting::obtain('telegram_diary')) {
             if (Setting::obtain('telegram_diary')) {
                 $jobs->sendTelegramDiary();
                 $jobs->sendTelegramDiary();
             }
             }

+ 0 - 1
src/Command/Tool.php

@@ -295,7 +295,6 @@ EOL;
             $user->uuid = Uuid::uuid4();
             $user->uuid = Uuid::uuid4();
             $user->api_token = Uuid::uuid4();
             $user->api_token = Uuid::uuid4();
             $user->port = Tools::getAvPort();
             $user->port = Tools::getAvPort();
-            $user->t = 0;
             $user->u = 0;
             $user->u = 0;
             $user->d = 0;
             $user->d = 0;
             $user->transfer_enable = 0;
             $user->transfer_enable = 0;

+ 4 - 0
src/Controllers/Admin/Setting/CronController.php

@@ -19,6 +19,10 @@ final class CronController extends BaseController
         'enable_monthly_finance_mail',
         'enable_monthly_finance_mail',
         'enable_detect_gfw',
         'enable_detect_gfw',
         'enable_detect_ban',
         'enable_detect_ban',
+        'enable_detect_inactive_user',
+        'detect_inactive_user_checkin_days',
+        'detect_inactive_user_login_days',
+        'detect_inactive_user_use_days',
     ];
     ];
 
 
     /**
     /**

+ 6 - 39
src/Controllers/Admin/UserController.php

@@ -8,8 +8,6 @@ use App\Controllers\AuthController;
 use App\Controllers\BaseController;
 use App\Controllers\BaseController;
 use App\Models\User;
 use App\Models\User;
 use App\Models\UserMoneyLog;
 use App\Models\UserMoneyLog;
-use App\Services\Auth;
-use App\Utils\Cookie;
 use App\Utils\Hash;
 use App\Utils\Hash;
 use App\Utils\Tools;
 use App\Utils\Tools;
 use Exception;
 use Exception;
@@ -17,7 +15,6 @@ use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
 use Slim\Http\Response;
 use Slim\Http\ServerRequest;
 use Slim\Http\ServerRequest;
 use function str_replace;
 use function str_replace;
-use function time;
 use const PHP_EOL;
 use const PHP_EOL;
 
 
 final class UserController extends BaseController
 final class UserController extends BaseController
@@ -33,8 +30,10 @@ final class UserController extends BaseController
             'transfer_enable' => '流量限制',
             'transfer_enable' => '流量限制',
             'transfer_used' => '当期用量',
             'transfer_used' => '当期用量',
             'class' => '等级',
             'class' => '等级',
+            'is_admin' => '是否管理员',
+            'is_banned' => '是否封禁',
+            'is_inactive' => '是否闲置',
             'reg_date' => '注册时间',
             'reg_date' => '注册时间',
-            'expire_in' => '账户过期',
             'class_expire' => '等级过期',
             'class_expire' => '等级过期',
         ],
         ],
         'create_dialog' => [
         'create_dialog' => [
@@ -241,41 +240,6 @@ final class UserController extends BaseController
         ]);
         ]);
     }
     }
 
 
-    public function changetouser(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
-    {
-        $userid = $request->getParam('userid');
-        $adminid = $request->getParam('adminid');
-        $user = User::find($userid);
-        $admin = User::find($adminid);
-        $expire_in = time() + 60 * 60;
-
-        if (! $admin->is_admin || ! $user || ! Auth::getUser()->isLogin) {
-            return $response->withJson([
-                'ret' => 0,
-                'msg' => '非法请求',
-            ]);
-        }
-
-        Cookie::set([
-            'uid' => $user->id,
-            'email' => $user->email,
-            'key' => Hash::cookieHash($user->pass, $expire_in),
-            'ip' => Hash::ipHash($_SERVER['REMOTE_ADDR'], $user->id, $expire_in),
-            'expire_in' => $expire_in,
-            'old_uid' => Cookie::get('uid'),
-            'old_email' => Cookie::get('email'),
-            'old_key' => Cookie::get('key'),
-            'old_ip' => Cookie::get('ip'),
-            'old_expire_in' => Cookie::get('expire_in'),
-            'old_local' => $request->getParam('local'),
-        ], $expire_in);
-
-        return $response->withJson([
-            'ret' => 1,
-            'msg' => '切换成功',
-        ]);
-    }
-
     public function ajax(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     public function ajax(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
     {
         $users = User::orderBy('id', 'desc')->get();
         $users = User::orderBy('id', 'desc')->get();
@@ -286,6 +250,9 @@ final class UserController extends BaseController
             <a class="btn btn-blue" href="/admin/user/' . $user->id . '/edit">编辑</a>';
             <a class="btn btn-blue" href="/admin/user/' . $user->id . '/edit">编辑</a>';
             $user->transfer_enable = $user->enableTraffic();
             $user->transfer_enable = $user->enableTraffic();
             $user->transfer_used = $user->usedTraffic();
             $user->transfer_used = $user->usedTraffic();
+            $user->is_admin = $user->is_admin === 1 ? '是' : '否';
+            $user->is_banned = $user->is_banned === 1 ? '是' : '否';
+            $user->is_inactive = $user->is_inactive === 1 ? '是' : '否';
         }
         }
 
 
         return $response->withJson([
         return $response->withJson([

+ 7 - 1
src/Controllers/AuthController.php

@@ -83,6 +83,7 @@ final class AuthController extends BaseController
         if (! Hash::checkPassword($user->pass, $passwd)) {
         if (! Hash::checkPassword($user->pass, $passwd)) {
             // 记录登录失败
             // 记录登录失败
             $user->collectLoginIP($_SERVER['REMOTE_ADDR'], 1);
             $user->collectLoginIP($_SERVER['REMOTE_ADDR'], 1);
+
             return $response->withJson([
             return $response->withJson([
                 'ret' => 0,
                 'ret' => 0,
                 'msg' => '邮箱或者密码错误',
                 'msg' => '邮箱或者密码错误',
@@ -91,6 +92,9 @@ final class AuthController extends BaseController
 
 
         if ($user->ga_enable === 1) {
         if ($user->ga_enable === 1) {
             if (strlen($code) !== 6) {
             if (strlen($code) !== 6) {
+                // 记录登录失败
+                $user->collectLoginIP($_SERVER['REMOTE_ADDR'], 1);
+
                 return $response->withJson([
                 return $response->withJson([
                     'ret' => 0,
                     'ret' => 0,
                     'msg' => '两步验证码错误',
                     'msg' => '两步验证码错误',
@@ -101,6 +105,9 @@ final class AuthController extends BaseController
             $rcode = $ga->verifyCode($user->ga_token, $code);
             $rcode = $ga->verifyCode($user->ga_token, $code);
 
 
             if (! $rcode) {
             if (! $rcode) {
+                // 记录登录失败
+                $user->collectLoginIP($_SERVER['REMOTE_ADDR'], 1);
+
                 return $response->withJson([
                 return $response->withJson([
                     'ret' => 0,
                     'ret' => 0,
                     'msg' => '两步验证码错误',
                     'msg' => '两步验证码错误',
@@ -253,7 +260,6 @@ final class AuthController extends BaseController
         $user->uuid = Uuid::uuid4();
         $user->uuid = Uuid::uuid4();
         $user->api_token = Uuid::uuid4();
         $user->api_token = Uuid::uuid4();
         $user->port = Tools::getAvPort();
         $user->port = Tools::getAvPort();
-        $user->t = 0;
         $user->u = 0;
         $user->u = 0;
         $user->d = 0;
         $user->d = 0;
         $user->method = $configs['sign_up_for_method'];
         $user->method = $configs['sign_up_for_method'];

+ 6 - 5
src/Controllers/WebAPI/UserController.php

@@ -150,14 +150,16 @@ final class UserController extends BaseController
         }
         }
 
 
         $pdo = DB::getPdo();
         $pdo = DB::getPdo();
-        $stat = $pdo->prepare('UPDATE user SET t = UNIX_TIMESTAMP(),
+        $stat = $pdo->prepare('
+                UPDATE user SET last_use_time = UNIX_TIMESTAMP(),
                 u = u + ?,
                 u = u + ?,
                 d = d + ?,
                 d = d + ?,
                 transfer_total = transfer_total + ?,
                 transfer_total = transfer_total + ?,
-                transfer_today = transfer_today + ? WHERE id = ?');
-
+                transfer_today = transfer_today + ? WHERE id = ?
+        ');
         $rate = (float) $node->traffic_rate;
         $rate = (float) $node->traffic_rate;
         $sum = 0;
         $sum = 0;
+
         foreach ($data as $log) {
         foreach ($data as $log) {
             $u = $log?->u;
             $u = $log?->u;
             $d = $log?->d;
             $d = $log?->d;
@@ -169,8 +171,7 @@ final class UserController extends BaseController
         }
         }
 
 
         $node->increment('node_bandwidth', $sum);
         $node->increment('node_bandwidth', $sum);
-
-        $node->online_user = count($data);
+        $node->online_user = count($data) - 1;
         $node->save();
         $node->save();
 
 
         return $response->withJson([
         return $response->withJson([

+ 5 - 2
src/Models/User.php

@@ -35,8 +35,8 @@ final class User extends Model
      * @var bool
      * @var bool
      */
      */
     public bool $isLogin;
     public bool $isLogin;
-    protected $connection = 'default';
 
 
+    protected $connection = 'default';
     protected $table = 'user';
     protected $table = 'user';
 
 
     /**
     /**
@@ -46,7 +46,6 @@ final class User extends Model
      */
      */
     protected $casts = [
     protected $casts = [
         'port' => 'int',
         'port' => 'int',
-        'is_admin' => 'boolean',
         'node_speedlimit' => 'float',
         'node_speedlimit' => 'float',
         'daily_mail_enable' => 'int',
         'daily_mail_enable' => 'int',
         'ref_by' => 'int',
         'ref_by' => 'int',
@@ -626,6 +625,10 @@ final class User extends Model
         $loginip->datetime = time();
         $loginip->datetime = time();
         $loginip->type = $type;
         $loginip->type = $type;
 
 
+        if ($type === 0) {
+            $this->last_login_time = time();
+        }
+
         return $loginip->save();
         return $loginip->save();
     }
     }
 }
 }

+ 2 - 2
src/Services/Analytics.php

@@ -95,12 +95,12 @@ final class Analytics
     public function getOnlineUser($time)
     public function getOnlineUser($time)
     {
     {
         $time = time() - $time;
         $time = time() - $time;
-        return User::where('t', '>', $time)->count();
+        return User::where('last_use_time', '>', $time)->count();
     }
     }
 
 
     public function getUnusedUser()
     public function getUnusedUser()
     {
     {
-        return User::where('t', '=', 0)->count();
+        return User::where('last_use_time', '=', 0)->count();
     }
     }
 
 
     public function getTotalNode()
     public function getTotalNode()

+ 28 - 1
src/Services/CronJob.php

@@ -61,7 +61,11 @@ final class CronJob
 
 
     public static function cleanDb(): void
     public static function cleanDb(): void
     {
     {
-        UserSubscribeLog::where('request_time', '<', date('Y-m-d H:i:s', time() - 86400 * (int) $_ENV['subscribeLog_keep_days']))->delete();
+        UserSubscribeLog::where(
+            'request_time',
+            '<',
+            date('Y-m-d H:i:s', time() - 86400 * (int) $_ENV['subscribeLog_keep_days'])
+        )->delete();
         UserHourlyUsage::where('datetime', '<', time() - 86400 * (int) $_ENV['trafficLog_keep_days'])->delete();
         UserHourlyUsage::where('datetime', '<', time() - 86400 * (int) $_ENV['trafficLog_keep_days'])->delete();
         DetectLog::where('datetime', '<', time() - 86400 * 3)->delete();
         DetectLog::where('datetime', '<', time() - 86400 * 3)->delete();
         EmailQueue::where('time', '<', time() - 86400)->delete();
         EmailQueue::where('time', '<', time() - 86400)->delete();
@@ -73,6 +77,29 @@ final class CronJob
         echo date('Y-m-d H:i:s') . ' 数据库清理完成' . PHP_EOL;
         echo date('Y-m-d H:i:s') . ' 数据库清理完成' . PHP_EOL;
     }
     }
 
 
+    public static function detectInactiveUser(): void
+    {
+        $checkin_days = Setting::obtain('detect_inactive_user_checkin_days');
+        $login_days = Setting::obtain('detect_inactive_user_login_days');
+        $use_days = Setting::obtain('detect_inactive_user_use_days');
+
+        User::where('is_admin', '=', '0')
+            ->where('is_inactive', '=', '0')
+            ->where('last_check_in_time', '<', time() - 86400 * $checkin_days)
+            ->where('last_login_time', '<', time() - 86400 * $login_days)
+            ->where('last_use_time', '<', time() - 86400 * $use_days)
+            ->update(['is_inactive' => 1]);
+
+        User::where('is_admin', '=', '0')
+            ->where('is_inactive', '=', '1')
+            ->where('last_check_in_time', '>', time() - 86400 * $checkin_days)
+            ->where('last_login_time', '>', time() - 86400 * $login_days)
+            ->where('last_use_time', '>', time() - 86400 * $use_days)
+            ->update(['is_inactive' => 0]);
+
+        echo date('Y-m-d H:i:s') . ' 检测到 ' . User::where('is_inactive', '=', '1')->count() . ' 个账户处于闲置状态' . PHP_EOL;
+    }
+
     /**
     /**
      * @throws TelegramSDKException
      * @throws TelegramSDKException
      */
      */

+ 1 - 1
src/Utils/Telegram/Commands/StartCommand.php

@@ -96,7 +96,7 @@ final class StartCommand extends Command
                 $BinsUser->im_value = $SendUser['username'];
                 $BinsUser->im_value = $SendUser['username'];
             }
             }
             $BinsUser->save();
             $BinsUser->save();
-            if ($BinsUser->is_admin >= 1) {
+            if ($BinsUser->is_admin === 1) {
                 $text = '尊敬的 **管理员** 你好,恭喜绑定成功。' . PHP_EOL . '当前绑定邮箱为: ' . $BinsUser->email;
                 $text = '尊敬的 **管理员** 你好,恭喜绑定成功。' . PHP_EOL . '当前绑定邮箱为: ' . $BinsUser->email;
             } else {
             } else {
                 if ($BinsUser->class >= 1) {
                 if ($BinsUser->class >= 1) {

+ 1 - 1
src/Utils/Telegram/Message.php

@@ -72,7 +72,7 @@ final class Message
                     $BinsUser->im_type = 4;
                     $BinsUser->im_type = 4;
                     $BinsUser->im_value = $this->triggerUser['username'];
                     $BinsUser->im_value = $this->triggerUser['username'];
                     $BinsUser->save();
                     $BinsUser->save();
-                    if ($BinsUser->is_admin >= 1) {
+                    if ($BinsUser->is_admin === 1) {
                         $text = '尊敬的**管理员**你好,恭喜绑定成功。' . PHP_EOL . '当前绑定邮箱为:' . $BinsUser->email;
                         $text = '尊敬的**管理员**你好,恭喜绑定成功。' . PHP_EOL . '当前绑定邮箱为:' . $BinsUser->email;
                     } else {
                     } else {
                         if ($BinsUser->class >= 1) {
                         if ($BinsUser->class >= 1) {