Sfoglia il codice sorgente

feat: user money log & apply giftcard

M1Screw 2 anni fa
parent
commit
4d2bfba1ea

+ 1 - 1
README.md

@@ -15,7 +15,7 @@
 [![Sonar Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=sspanel-uim_SSPanel-Uim-Dev&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=sspanel-uim_SSPanel-Uim-Dev)
 [![Sonar Security Rating](https://sonarcloud.io/api/project_badges/measure?project=sspanel-uim_SSPanel-Uim-Dev&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=sspanel-uim_SSPanel-Uim-Dev)
 
-[配套Trojan后端](https://github.com/sspanel-uim/TrojanX) | [Telegram 水群](https://t.me/ssunion) | [Telegram 通知频道](https://t.me/sspanel_uim) | [Telegram 开发频道](https://t.me/sspanel_uim_dev) | [Dev Blog](https://blog.sspanel.org)
+[配套Trojan后端](https://github.com/sspanel-uim/TrojanX) | [Telegram 水群](https://t.me/ssunion) | [Telegram 通知频道](https://t.me/sspanel_uim) | [Telegram 开发频道](https://t.me/sspanel_uim_dev) | [Discord Dev Center](https://discord.gg/A7uFKCvf8V) | [Dev Blog](https://blog.sspanel.org)
 
 ## 简介
 

+ 5 - 1
app/routes.php

@@ -33,7 +33,7 @@ return static function (Slim\App $app): void {
         // 封禁
         $group->get('/banned', App\Controllers\UserController::class . ':banned');
         // 节点
-        $group->get('/server', App\Controllers\User\ServerController::class . ':userServerPage');
+        $group->get('/server', App\Controllers\User\ServerController::class . ':server');
         // 审计
         $group->get('/detect', App\Controllers\User\DetectController::class . ':index');
         // 工单
@@ -72,6 +72,10 @@ return static function (Slim\App $app): void {
         // 记录
         $group->get('/subscribe/log', App\Controllers\User\LogController::class . ':subscribe');
         $group->get('/detect/log', App\Controllers\User\LogController::class . ':detect');
+        // 账户余额
+        $group->get('/money', App\Controllers\User\MoneyController::class . ':money');
+        // 礼品卡兑换
+        $group->post('/giftcard', App\Controllers\User\MoneyController::class . ':applyGiftCard');
         // 产品页面
         $group->get('/product', App\Controllers\User\ProductController::class . ':product');
         // 订单页面

+ 36 - 35
composer.lock

@@ -123,16 +123,16 @@
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.263.12",
+            "version": "3.268.16",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "8b1c7b4f1f931ee480a31f37870bdc66f9c23248"
+                "reference": "b59134c9ca64dcb9de6f7dbbcb9d5a75ed665a98"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8b1c7b4f1f931ee480a31f37870bdc66f9c23248",
-                "reference": "8b1c7b4f1f931ee480a31f37870bdc66f9c23248",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b59134c9ca64dcb9de6f7dbbcb9d5a75ed665a98",
+                "reference": "b59134c9ca64dcb9de6f7dbbcb9d5a75ed665a98",
                 "shasum": ""
             },
             "require": {
@@ -142,7 +142,7 @@
                 "ext-simplexml": "*",
                 "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
                 "guzzlehttp/promises": "^1.4.0",
-                "guzzlehttp/psr7": "^1.8.5 || ^2.3",
+                "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
                 "mtdowling/jmespath.php": "^2.6",
                 "php": ">=5.5"
             },
@@ -161,6 +161,7 @@
                 "paragonie/random_compat": ">= 2",
                 "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5",
                 "psr/cache": "^1.0",
+                "psr/http-message": "^1.0",
                 "psr/simple-cache": "^1.0",
                 "sebastian/comparator": "^1.2.3 || ^4.0",
                 "yoast/phpunit-polyfills": "^1.0"
@@ -211,9 +212,9 @@
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.263.12"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.268.16"
             },
-            "time": "2023-04-17T18:22:13+00:00"
+            "time": "2023-04-21T21:37:05+00:00"
         },
         {
             "name": "bacon/bacon-qr-code",
@@ -1172,7 +1173,7 @@
         },
         {
             "name": "illuminate/collections",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/collections.git",
@@ -1227,7 +1228,7 @@
         },
         {
             "name": "illuminate/conditionable",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/conditionable.git",
@@ -1273,7 +1274,7 @@
         },
         {
             "name": "illuminate/container",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/container.git",
@@ -1324,7 +1325,7 @@
         },
         {
             "name": "illuminate/contracts",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/contracts.git",
@@ -1372,7 +1373,7 @@
         },
         {
             "name": "illuminate/database",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/database.git",
@@ -1442,7 +1443,7 @@
         },
         {
             "name": "illuminate/macroable",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/macroable.git",
@@ -1488,7 +1489,7 @@
         },
         {
             "name": "illuminate/pagination",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/pagination.git",
@@ -1538,16 +1539,16 @@
         },
         {
             "name": "illuminate/support",
-            "version": "v9.52.5",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/support.git",
-                "reference": "199e5250735d0c49fe0385360451659690ff71a3"
+                "reference": "5c38d755c5fa767d5b689867888ee4e5f53fc8a6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/support/zipball/199e5250735d0c49fe0385360451659690ff71a3",
-                "reference": "199e5250735d0c49fe0385360451659690ff71a3",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/5c38d755c5fa767d5b689867888ee4e5f53fc8a6",
+                "reference": "5c38d755c5fa767d5b689867888ee4e5f53fc8a6",
                 "shasum": ""
             },
             "require": {
@@ -1605,7 +1606,7 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-03-13T01:37:31+00:00"
+            "time": "2023-04-04T18:35:58+00:00"
         },
         {
             "name": "irazasyed/telegram-bot-sdk",
@@ -7673,16 +7674,16 @@
         },
         {
             "name": "phpstan/phpdoc-parser",
-            "version": "1.18.1",
+            "version": "1.20.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpdoc-parser.git",
-                "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f"
+                "reference": "57f6787f0bb6431905a18aa7caea25dcd2bd59e0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
-                "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/57f6787f0bb6431905a18aa7caea25dcd2bd59e0",
+                "reference": "57f6787f0bb6431905a18aa7caea25dcd2bd59e0",
                 "shasum": ""
             },
             "require": {
@@ -7712,9 +7713,9 @@
             "description": "PHPDoc parser with support for nullable, intersection and generic types",
             "support": {
                 "issues": "https://github.com/phpstan/phpdoc-parser/issues",
-                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1"
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.1"
             },
-            "time": "2023-04-07T11:51:11+00:00"
+            "time": "2023-04-22T09:05:52+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
@@ -8003,32 +8004,32 @@
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "8.10.0",
+            "version": "8.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "c4e213e6e57f741451a08e68ef838802eec92287"
+                "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/c4e213e6e57f741451a08e68ef838802eec92287",
-                "reference": "c4e213e6e57f741451a08e68ef838802eec92287",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/91428d5bcf7db93a842bcf97f465edf62527f3ea",
+                "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea",
                 "shasum": ""
             },
             "require": {
                 "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
                 "php": "^7.2 || ^8.0",
-                "phpstan/phpdoc-parser": ">=1.18.0 <1.19.0",
+                "phpstan/phpdoc-parser": ">=1.20.0 <1.21.0",
                 "squizlabs/php_codesniffer": "^3.7.1"
             },
             "require-dev": {
                 "phing/phing": "2.17.4",
                 "php-parallel-lint/php-parallel-lint": "1.3.2",
-                "phpstan/phpstan": "1.4.10|1.10.11",
+                "phpstan/phpstan": "1.10.14",
                 "phpstan/phpstan-deprecation-rules": "1.1.3",
-                "phpstan/phpstan-phpunit": "1.0.0|1.3.11",
+                "phpstan/phpstan-phpunit": "1.3.11",
                 "phpstan/phpstan-strict-rules": "1.5.1",
-                "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.0.19"
+                "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.1.1"
             },
             "type": "phpcodesniffer-standard",
             "extra": {
@@ -8052,7 +8053,7 @@
             ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.10.0"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.11.0"
             },
             "funding": [
                 {
@@ -8064,7 +8065,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-10T07:39:29+00:00"
+            "time": "2023-04-21T15:51:44+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",

+ 11 - 20
config/.config.example.php

@@ -1,14 +1,14 @@
 <?php
 
 //基本设置--------------------------------------------------------------------------------------------
-$_ENV['key']        = 'ChangeMe';                     //请务必修改此key为随机字符串
-$_ENV['pwdMethod']  = 'bcrypt';                       //密码加密 可选 md5, sha256, bcrypt, argon2i, argon2id(argon2i需要至少php7.2)
+$_ENV['key']        = 'ChangeMe';                     //Cookie加密密钥,请务必修改此key为随机字符串
+$_ENV['pwdMethod']  = 'bcrypt';                       //密码加密 可选 md5, sha256, bcrypt, argon2i, argon2id
 $_ENV['salt']       = '';                             //推荐配合 md5/sha256, bcrypt/argon2i/argon2id 会忽略此项
 
 $_ENV['debug']      = false;                          //debug模式开关,生产环境请保持为false
 $_ENV['appName']    = 'SSPanel-UIM';                  //站点名称
 $_ENV['baseUrl']    = 'https://example.com';          //站点地址
-$_ENV['muKey']      = 'ChangeMe';                      //WebAPI密钥,用于节点服务端与面板通信,请务必修改此key为随机字符串
+$_ENV['muKey']      = 'ChangeMe';                     //WebAPI密钥,用于节点服务端与面板通信,请务必修改此key为随机字符串
 
 //数据库设置--------------------------------------------------------------------------------------------
 // db_host|db_socket 二选一,若设置 db_socket 则 db_host 会被忽略,不用请留空。若数据库在本机上推荐用 db_socket。
@@ -26,12 +26,6 @@ $_ENV['db_charset']   = 'utf8mb4';
 $_ENV['db_collation'] = 'utf8mb4_unicode_ci';
 $_ENV['db_prefix']    = '';
 
-//流媒体解锁 如下设置将使397,297号节点复用4号节点的检测结果 使用时去掉注释符 //
-$_ENV['streaming_media_unlock_multiplexing'] = [
-    //'397' => '4',
-    //'297' => '4',
-];
-
 //邮件设置--------------------------------------------------------------------------------------------
 $_ENV['mail_filter']        = 0;            //0: 关闭; 1: 白名单模式; 2; 黑名单模式;
 $_ENV['mail_filter_list']   = array("qq.com", "vip.qq.com", "foxmail.com");
@@ -46,9 +40,6 @@ $_ENV['auto_clean_uncheck_days']    = -1;           //自动清理多少天没
 $_ENV['auto_clean_unused_days']     = -1;           //自动清理多少天没使用的0级用户,小于等于0时关闭
 $_ENV['auto_clean_min_money']       = 1;            //余额低于多少的0级用户可以被清理
 
-$_ENV['enable_bought_reset']        = true;         //购买时是否重置流量
-$_ENV['enable_bought_extend']       = true;         //购买时是否延长等级期限(同等级配套)
-
 #高级
 $_ENV['class_expire_reset_traffic'] = 0;            //等级到期时重置为的流量值,单位GB,小于0时不重置
 $_ENV['account_expire_delete_days'] = -1;           //账户到期几天之后会删除账户,小于0时不删除
@@ -62,7 +53,7 @@ $_ENV['notify_limit_value']         = 20;           //当上一项为per时,
 
 //日志设置---------------------------------------------------------------------------------------
 $_ENV['trafficLog']               = false;                          //是否记录用户每小时使用流量
-$_ENV['trafficLog_keep_days']     = 14;                             //每小时使用流量记录保留天数
+$_ENV['trafficLog_keep_days']     = 7;                             //每小时使用流量记录保留天数
 
 $_ENV['subscribeLog']               = false;                        //是否记录用户订阅日志
 $_ENV['subscribeLog_keep_days']     = 7;                            //订阅记录保留天数
@@ -113,14 +104,14 @@ $_ENV['enable_telegram']                    = false;        //是否开启 Teleg
 $_ENV['telegram_token']                     = '';           //Telegram bot,bot 的 token ,跟 father bot 申请
 $_ENV['telegram_chatid']                    = -111;         //Telegram bot,群组会话 ID,把机器人拉进群里之后跟他 /ping 一下即可得到
 $_ENV['telegram_bot']                       = '_bot';       //Telegram 机器人账号
-$_ENV['telegram_request_token']             = '';           //Telegram 机器人请求Key,随意设置,由大小写英文和数字组成,更新这个参数之后请 php xcat Tool setTelegram
+$_ENV['telegram_request_token']             = '';           //Webhook密钥,更新这个参数之后请 php xcat Tool setTelegram
 
 //节点检测-----------------------------------------------------------------------------------------------
 #GFW检测,请通过crontab进行【开启/关闭】
-$_ENV['detect_gfw_interval']             = 3600;                                                               //检测间隔,单位:秒,低于推荐值会爆炸
-$_ENV['detect_gfw_port']                 = 22;                                                                 //所有节点服务器都打开的TCP端口,常用的为22(SSH端口)
-$_ENV['detect_gfw_url']                  = 'http://cn-sh-tcping.sspanel.org:8080/tcping?ip={ip}&port={port}'; //检测节点是否被gfw墙了的API的URL
-$_ENV['detect_gfw_count']                = '3';                                                                //尝试次数
+$_ENV['detect_gfw_interval'] = 3600;                                                 //检测间隔,单位:秒,低于推荐值会爆炸
+$_ENV['detect_gfw_port']     = 443;                                                  //所有节点服务器都打开的TCP端口
+$_ENV['detect_gfw_url']      = 'http://example.com:8080/tcping?ip={ip}&port={port}'; //检测节点是否被gfw墙了的API的URL
+$_ENV['detect_gfw_count']    = '3';                                                  //尝试次数
 
 #离线检测
 $_ENV['enable_detect_offline']           = true;
@@ -133,7 +124,7 @@ $_ENV['enable_detect_offline']           = true;
 $_ENV['WebAPI']     = true;
 
 #杂项
-$_ENV['authDriver']             = 'cookie';            //不能更改此项
+$_ENV['authDriver']             = 'cookie';            //可选: cookie
 $_ENV['sessionDriver']          = 'cookie';            //可选: cookie
 $_ENV['cacheDriver']            = 'cookie';            //可选: cookie
 $_ENV['tokenDriver']            = 'db';                //可选: db
@@ -141,7 +132,7 @@ $_ENV['tokenDriver']            = 'db';                //可选: db
 $_ENV['enable_login_bind_ip']   = false;        //是否将登陆线程和IP绑定
 $_ENV['rememberMeDuration']     = 7;           //登录时记住账号时长天数
 
-$_ENV['timeZone']               = 'PRC';                 //PRC 天朝时间  UTC 格林时间
+$_ENV['timeZone']               = 'Asia/Taipei';         //需使用 PHP 兼容的时区格式
 $_ENV['theme']                  = 'tabler';              //默认主题
 $_ENV['jump_delay']             = 1200;                  //跳转延时,单位ms,不建议太长
 

+ 0 - 4
resources/views/tabler/admin/giftcard.tpl

@@ -21,10 +21,6 @@
                             <i class="icon ti ti-plus"></i>
                             创建
                         </a>
-                        <a href="#" class="btn btn-primary d-sm-none btn-icon" data-bs-toggle="modal"
-                            data-bs-target="#create-dialog">
-                            <i class="icon ti ti-plus"></i>
-                        </a>
                     </div>
                 </div>
             </div>

+ 2 - 2
resources/views/tabler/admin/tabler_header.tpl

@@ -55,9 +55,9 @@
                         </a>
                         <div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
                             {if $user->is_dark_mode}
-                            <a id="switch_theme_mode" class="dropdown-item">切换至浅色模式</a>
+                            <a id="switch_theme_mode" class="dropdown-item">浅色模式</a>
                             {else}
-                            <a id="switch_theme_mode" class="dropdown-item">切换至深色模式</a>
+                            <a id="switch_theme_mode" class="dropdown-item">深色模式</a>
                             {/if}
                             <a href="/user/logout" class="dropdown-item">登出</a>
                         </div>

+ 239 - 260
resources/views/tabler/user/index.tpl

@@ -26,7 +26,7 @@
                                     <div class="row align-items-center">
                                         <div class="col-auto">
                                             <span class="bg-blue text-white avatar">
-                                                <i class="ti ti-star icon"></i>
+                                                <i class="ti ti-vip icon"></i>
                                             </span>
                                         </div>
                                         <div class="col">
@@ -34,7 +34,11 @@
                                                 账户等级
                                             </div>
                                             <div class="text-muted">
-                                                LV. {$user->class}
+                                                {if $user->class === 0}
+                                                    免费
+                                                {else}
+                                                    Lv. {$user->class}
+                                                {/if}
                                             </div>
                                         </div>
                                     </div>
@@ -114,278 +118,253 @@
                         </div>
                     </div>
                 </div>
-                <div class="col-lg-6">
-                    <div class="row row-cards">
-                        <div class="col-12">
-                            <div class="card">
-                                <ul class="nav nav-tabs nav-fill" data-bs-toggle="tabs">
-                                    <li class="nav-item">
-                                        <a href="#sub" class="nav-link active" data-bs-toggle="tab">
-                                            <i class="ti ti-rss icon"></i>
-                                            &nbsp;通用订阅
-                                        </a>
-                                    </li>
-                                    {if $public_setting['enable_traditional_sub']}
-                                    <li class="nav-item">
-                                        <a href="#traditional-sub" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-rss icon"></i>
-                                            &nbsp;传统订阅
-                                        </a>
-                                    </li>
-                                    {/if}
-                                    <li class="nav-item">
-                                        <a href="#windows" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-brand-windows icon"></i>
-                                            &nbsp;Windows
-                                        </a>
-                                    </li>
-                                    <li class="nav-item">
-                                        <a href="#macos" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-brand-finder icon"></i>
-                                            &nbsp;Macos
-                                        </a>
-                                    </li>
-                                    <li class="nav-item">
-                                        <a href="#android" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-brand-android icon"></i>
-                                            &nbsp;Android
-                                        </a>
-                                    </li>
-                                    <li class="nav-item">
-                                        <a href="#ios" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-brand-apple icon"></i>
-                                            &nbsp;IOS
-                                        </a>
-                                    </li>
-                                    <li class="nav-item">
-                                        <a href="#linux" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-brand-redhat icon"></i>
-                                            &nbsp;Linux
-                                        </a>
-                                    </li>
-                                    <li class="nav-item">
-                                        <a href="#config" class="nav-link" data-bs-toggle="tab">
-                                            <i class="ti ti-file-text icon"></i>
-                                            &nbsp;Config
-                                        </a>
-                                    </li>
-                                </ul>
-                                <div class="card-body">
-                                    <div class="tab-content">
-                                        <div class="tab-pane active show" id="sub">
-                                            <div>
-                                                <p>
-                                                    通用订阅(json):<code>{$UniversalSub}/json</code>
-                                                </p>
-                                                <p>
-                                                    通用订阅(clash):<code>{$UniversalSub}/clash</code>
-                                                </p>
-                                                {if $public_setting['enable_ss_sub']}
-                                                <p>
-                                                    通用订阅(sip008):<code>{$UniversalSub}/sip008</code>
-                                                </p>
-                                                {/if}
-                                                <div class="btn-list justify-content-start">
-                                                    <a data-clipboard-text="{$UniversalSub}/json"
-                                                        class="copy btn btn-primary">
-                                                        复制通用订阅(json)
-                                                    </a>
-                                                    <a data-clipboard-text="{$UniversalSub}/clash"
-                                                        class="copy btn btn-primary">
-                                                        复制通用订阅(clash)
-                                                    </a>
-                                                    {if $public_setting['enable_ss_sub']}
-                                                    <a data-clipboard-text="{$UniversalSub}/sip008"
-                                                       class="copy btn btn-primary">
-                                                        复制通用订阅(sip008)
-                                                    </a>
-                                                    {/if}
-                                                </div>
-                                            </div>
-                                        </div>
-                                        {if $public_setting['enable_traditional_sub']}
-                                        <div class="tab-pane show" id="traditional-sub">
-                                            <div>
-                                                {if $public_setting['enable_ss_sub']}
-                                                <p>
-                                                    传统订阅(Shadowsocks):<code>{$TraditionalSub}?ss=1</code>
-                                                </p>
-                                                <p>
-                                                    传统订阅(Shadowsocks SIP002):<code>{$TraditionalSub}?sip002=1</code>
-                                                </p>
-                                                {/if}
-                                                {if $public_setting['enable_v2_sub']}
-                                                <p>
-                                                    传统订阅(V2Ray):<code>{$TraditionalSub}?v2ray=1</code>
-                                                </p>
-                                                {/if}
-                                                {if $public_setting['enable_trojan_sub']}
-                                                <p>
-                                                    传统订阅(Trojan):<code>{$TraditionalSub}?trojan=1</code>
-                                                </p>
-                                                {/if}
-                                                <div class="btn-list justify-content-start">
-                                                    {if $public_setting['enable_ss_sub']}
-                                                    <a data-clipboard-text="{$TraditionalSub}?ss=1"
-                                                        class="copy btn btn-primary">
-                                                        复制传统订阅(Shadowsocks)
-                                                    </a>
-                                                    <a data-clipboard-text="{$TraditionalSub}?sip002=1"
-                                                        class="copy btn btn-primary">
-                                                        复制传统订阅(Shadowsocks SIP002)
-                                                    </a>
-                                                    {/if}
-                                                    {if $public_setting['enable_v2_sub']}
-                                                    <a data-clipboard-text="{$TraditionalSub}?v2ray=1"
-                                                        class="copy btn btn-primary">
-                                                        复制传统订阅(V2Ray)
-                                                    </a>
-                                                    {/if}
-                                                    {if $public_setting['enable_trojan_sub']}
-                                                    <a data-clipboard-text="{$TraditionalSub}?trojan=1"
-                                                        class="copy btn btn-primary">
-                                                        复制传统订阅(Trojan)
-                                                    </a>
-                                                    {/if}
-                                                    <a href="/clients/v2rayN-Core.zip"
-                                                        class="btn btn-primary">
-                                                        下载 v2rayN(Windows)
-                                                    </a>
-                                                    <a href="/clients/v2rayNG.apk"
-                                                        class="btn btn-primary">
-                                                        下载 v2rayNG(Android)
-                                                    </a>
-                                                </div>
-                                            </div>
-                                        </div>
-                                        {/if}
-                                        <div class="tab-pane" id="windows">
-                                            <div>
-                                                <p>
-                                                    适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
-                                                </p>
-                                                <div class="btn-list justify-content-start">
-                                                    <a data-clipboard-text="{$UniversalSub}/clash"
-                                                        class="copy btn btn-primary">
-                                                        复制 Clash 订阅链接
-                                                    </a>
-                                                    <a href="/clients/Clash-Windows.exe"
-                                                        class="btn btn-primary">
-                                                        下载 Clash for Windows
-                                                    </a>
-                                                    <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
-                                                        class="btn btn-primary">
-                                                        导入 Clash
-                                                    </a>
-                                                </div>
-                                            </div>
-                                        </div>
-                                        <div class="tab-pane" id="macos">
-                                            <p>
-                                                适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
-                                            </p>
-                                            <div class="btn-list justify-content-start">
-                                                <a data-clipboard-text="{$UniversalSub}/clash"
-                                                    class="copy btn btn-primary">
-                                                    复制 Clash 订阅链接
-                                                </a>
-                                                <a href="/clients/Clash-Windows.dmg"
-                                                    class="btn btn-primary">
-                                                    下载 Clash for Windows
-                                                </a>
-                                                <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
-                                                    class="btn btn-primary">
-                                                    导入 Clash
-                                                </a>
-                                            </div>
-                                        </div>
-                                        <div class="tab-pane" id="android">
-                                            <p>
-                                                适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
-                                            </p>
-                                            <div class="btn-list justify-content-start">
-                                                <a data-clipboard-text="{$UniversalSub}/clash"
-                                                    class="copy btn btn-primary">
-                                                    复制 Clash 订阅链接
-                                                </a>
-                                                <a href="/clients/Clash-Android.apk"
-                                                    class="btn btn-primary">
-                                                    下载 Clash for Android
-                                                </a>
-                                                <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
-                                                    class="btn btn-primary">
-                                                    导入 Clash
-                                                </a>
-                                            </div>
-                                        </div>
-                                        <div class="tab-pane" id="ios">
-                                            <p>
-                                                适用于 Clash 兼容客户端的订阅:<code>{$UniversalSub}/clash</code>
-                                            </p>
+                <div class="col-lg-6 col-sm-12">
+                    <div class="card">
+                        <ul class="nav nav-tabs nav-fill" data-bs-toggle="tabs">
+                            <li class="nav-item">
+                                <a href="#sub" class="nav-link active" data-bs-toggle="tab">
+                                    <i class="ti ti-rss icon"></i>
+                                    &nbsp;通用订阅
+                                </a>
+                            </li>
+                            {if $public_setting['enable_traditional_sub']}
+                                <li class="nav-item">
+                                    <a href="#traditional-sub" class="nav-link" data-bs-toggle="tab">
+                                        <i class="ti ti-rss icon"></i>
+                                        &nbsp;传统订阅
+                                    </a>
+                                </li>
+                            {/if}
+                            <li class="nav-item">
+                                <a href="#windows" class="nav-link" data-bs-toggle="tab">
+                                    <i class="ti ti-brand-windows icon"></i>
+                                    &nbsp;Windows
+                                </a>
+                            </li>
+                            <li class="nav-item">
+                                <a href="#macos" class="nav-link" data-bs-toggle="tab">
+                                    <i class="ti ti-brand-finder icon"></i>
+                                    &nbsp;MacOS
+                                </a>
+                            </li>
+                            <li class="nav-item">
+                                <a href="#android" class="nav-link" data-bs-toggle="tab">
+                                    <i class="ti ti-brand-android icon"></i>
+                                    &nbsp;Android
+                                </a>
+                            </li>
+                            <li class="nav-item">
+                                <a href="#ios" class="nav-link" data-bs-toggle="tab">
+                                    <i class="ti ti-brand-apple icon"></i>
+                                    &nbsp;iOS
+                                </a>
+                            </li>
+                            <li class="nav-item">
+                                <a href="#linux" class="nav-link" data-bs-toggle="tab">
+                                    <i class="ti ti-brand-redhat icon"></i>
+                                    &nbsp;Linux
+                                </a>
+                            </li>
+                            <li class="nav-item">
+                                <a href="#config" class="nav-link" data-bs-toggle="tab">
+                                    <i class="ti ti-file-text icon"></i>
+                                    &nbsp;Config
+                                </a>
+                            </li>
+                        </ul>
+                        <div class="card-body">
+                            <div class="tab-content">
+                                <div class="tab-pane active show" id="sub">
+                                    <div>
+                                        <p>
+                                            通用订阅(json):<code>{$UniversalSub}/json</code>
+                                        </p>
+                                        <p>
+                                            通用订阅(clash):<code>{$UniversalSub}/clash</code>
+                                        </p>
+                                        {if $public_setting['enable_ss_sub']}
                                             <p>
-                                                在购买并安装 Clash 兼容客户端(比如 Stash)之后,点击<code>复制 Clash 订阅链接</code>按钮,然后打开 Clash 兼容客户端导入即可。
+                                                通用订阅(sip008):<code>{$UniversalSub}/sip008</code>
                                             </p>
-                                            <div class="btn-list justify-content-start">
-                                                <a href="https://apps.apple.com/app/stash/id1596063349" target="_blank"
-                                                    class="btn btn-primary">
-                                                    购买 Stash
-                                                </a>
-                                                <a data-clipboard-text="{$UniversalSub}/clash"
-                                                    class="copy btn btn-primary">
-                                                    复制 Clash 订阅链接
-                                                </a>
-                                                <a href="stash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
-                                                    class="btn btn-primary">
-                                                    导入 Stash
+                                        {/if}
+                                        <div class="btn-list justify-content-start">
+                                            <a data-clipboard-text="{$UniversalSub}/json"
+                                               class="copy btn btn-primary">
+                                                复制通用订阅(json)
+                                            </a>
+                                            <a data-clipboard-text="{$UniversalSub}/clash"
+                                               class="copy btn btn-primary">
+                                                复制通用订阅(clash)
+                                            </a>
+                                            {if $public_setting['enable_ss_sub']}
+                                                <a data-clipboard-text="{$UniversalSub}/sip008"
+                                                   class="copy btn btn-primary">
+                                                    复制通用订阅(sip008)
                                                 </a>
-                                            </div>
+                                            {/if}
                                         </div>
-                                        <div class="tab-pane" id="linux">
-                                            <p>
-                                                适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
+                                    </div>
+                                </div>
+                                {if $public_setting['enable_traditional_sub']}
+                                    <div class="tab-pane show" id="traditional-sub">
+                                        <div>
+                                            {if $public_setting['enable_ss_sub']}<p>
+                                                传统订阅(Shadowsocks):<code>{$TraditionalSub}?ss=1</code></p><p>
+                                                传统订阅(Shadowsocks SIP002):<code>{$TraditionalSub}?sip002=1</code>
                                             </p>
+                                            {/if}
+                                            {if $public_setting['enable_v2_sub']}<p>
+                                                传统订阅(V2Ray):<code>{$TraditionalSub}?v2ray=1</code></p>
+                                            {/if}
+                                            {if $public_setting['enable_trojan_sub']}<p>
+                                                传统订阅(Trojan):<code>{$TraditionalSub}?trojan=1</code></p>
+                                            {/if}
                                             <div class="btn-list justify-content-start">
-                                                <a data-clipboard-text="{$UniversalSub}/clash"
-                                                    class="copy btn btn-primary">
-                                                    复制 Clash 订阅链接
+                                                {if $public_setting['enable_ss_sub']}<a data-clipboard-text="{$TraditionalSub}?ss=1"class="copy btn btn-primary">复制传统订阅(Shadowsocks)</a><a data-clipboard-text="{$TraditionalSub}?sip002=1" class="copy btn btn-primary">
+                                                    复制传统订阅(Shadowsocks SIP002)
                                                 </a>
-                                                <a href="/clients/Clash-Windows.tar.gz"
-                                                    class="btn btn-primary">
-                                                    下载 Clash for Windows
+                                                {/if}
+                                                {if $public_setting['enable_v2_sub']}<a data-clipboard-text="{$TraditionalSub}?v2ray=1"class="copy btn btn-primary">复制传统订阅(V2Ray)</a>
+                                                {/if}
+                                                {if $public_setting['enable_trojan_sub']}<a data-clipboard-text="{$TraditionalSub}?trojan=1"class="copy btn btn-primary">复制传统订阅(Trojan)</a>
+                                                {/if}
+                                                <a href="/clients/v2rayN-Core.zip"
+                                                   class="btn btn-primary">
+                                                    下载 v2rayN(Windows)
                                                 </a>
-                                                <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
-                                                    class="btn btn-primary">
-                                                    导入 Clash
+                                                <a href="/clients/v2rayNG.apk"
+                                                   class="btn btn-primary">
+                                                    下载 v2rayNG(Android)
                                                 </a>
                                             </div>
                                         </div>
-                                        <div class="tab-pane" id="config">
-                                            <p>您的连接信息:</p>
-                                            <div class="table-responsive">
-                                                <table class="table table-vcenter card-table">
-                                                    <tbody>
-                                                    <tr>
-                                                        <td><strong>端口</strong></td>
-                                                        <td>{$user->port}</td>
-                                                    </tr>
-                                                    <tr>
-                                                        <td><strong>连接密码</strong></td>
-                                                        <td>{$user->passwd}</td>
-                                                    </tr>
-                                                    <tr>
-                                                        <td><strong>UUID</strong></td>
-                                                        <td>{$user->uuid}</td>
-                                                    </tr>
-                                                    <tr>
-                                                        <td><strong>自定义加密</strong></td>
-                                                        <td>{$user->method}</td>
-                                                    </tr>
-                                                    </tbody>
-                                                </table>
-                                            </div>
+                                    </div>
+                                {/if}
+                                <div class="tab-pane" id="windows">
+                                    <div>
+                                        <p>
+                                            适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
+                                        </p>
+                                        <div class="btn-list justify-content-start">
+                                            <a data-clipboard-text="{$UniversalSub}/clash"
+                                               class="copy btn btn-primary">
+                                                复制 Clash 订阅链接
+                                            </a>
+                                            <a href="/clients/Clash-Windows.exe"
+                                               class="btn btn-primary">
+                                                下载 Clash for Windows
+                                            </a>
+                                            <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
+                                               class="btn btn-primary">
+                                                导入 Clash
+                                            </a>
                                         </div>
                                     </div>
                                 </div>
+                                <div class="tab-pane" id="macos">
+                                    <p>
+                                        适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
+                                    </p>
+                                    <div class="btn-list justify-content-start">
+                                        <a data-clipboard-text="{$UniversalSub}/clash"
+                                           class="copy btn btn-primary">
+                                            复制 Clash 订阅链接
+                                        </a>
+                                        <a href="/clients/Clash-Windows.dmg"
+                                           class="btn btn-primary">
+                                            下载 Clash for Windows
+                                        </a>
+                                        <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
+                                           class="btn btn-primary">
+                                            导入 Clash
+                                        </a>
+                                    </div>
+                                </div>
+                                <div class="tab-pane" id="android">
+                                    <p>
+                                        适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
+                                    </p>
+                                    <div class="btn-list justify-content-start">
+                                        <a data-clipboard-text="{$UniversalSub}/clash"
+                                           class="copy btn btn-primary">
+                                            复制 Clash 订阅链接
+                                        </a>
+                                        <a href="/clients/Clash-Android.apk"
+                                           class="btn btn-primary">
+                                            下载 Clash for Android
+                                        </a>
+                                        <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
+                                           class="btn btn-primary">
+                                            导入 Clash
+                                        </a>
+                                    </div>
+                                </div>
+                                <div class="tab-pane" id="ios">
+                                    <p>
+                                        适用于 Clash 兼容客户端的订阅:<code>{$UniversalSub}/clash</code>
+                                    </p>
+                                    <p>
+                                        在购买并安装 Clash 兼容客户端(比如 Stash)之后,点击<code>复制 Clash 订阅链接</code>按钮,然后打开 Clash 兼容客户端导入即可。
+                                    </p>
+                                    <div class="btn-list justify-content-start">
+                                        <a href="https://apps.apple.com/app/stash/id1596063349" target="_blank"
+                                           class="btn btn-primary">
+                                            购买 Stash
+                                        </a>
+                                        <a data-clipboard-text="{$UniversalSub}/clash"
+                                           class="copy btn btn-primary">
+                                            复制 Clash 订阅链接
+                                        </a>
+                                        <a href="stash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
+                                           class="btn btn-primary">
+                                            导入 Stash
+                                        </a>
+                                    </div>
+                                </div>
+                                <div class="tab-pane" id="linux">
+                                    <p>
+                                        适用于 Clash 的订阅:<code>{$UniversalSub}/clash</code>
+                                    </p>
+                                    <div class="btn-list justify-content-start">
+                                        <a data-clipboard-text="{$UniversalSub}/clash"
+                                           class="copy btn btn-primary">
+                                            复制 Clash 订阅链接
+                                        </a>
+                                        <a href="/clients/Clash-Windows.tar.gz"
+                                           class="btn btn-primary">
+                                            下载 Clash for Windows
+                                        </a>
+                                        <a href="clash://install-config?url={$UniversalSub}/clash&name={$config['appName']}"
+                                           class="btn btn-primary">
+                                            导入 Clash
+                                        </a>
+                                    </div>
+                                </div>
+                                <div class="tab-pane" id="config">
+                                    <p>您的连接信息:</p>
+                                    <div class="table-responsive">
+                                        <table class="table table-vcenter card-table">
+                                            <tbody>
+                                            <tr>
+                                                <td><strong>端口</strong></td>
+                                                <td>{$user->port}</td>
+                                            </tr>
+                                            <tr>
+                                                <td><strong>连接密码</strong></td>
+                                                <td>{$user->passwd}</td>
+                                            </tr>
+                                            <tr>
+                                                <td><strong>UUID</strong></td>
+                                                <td>{$user->uuid}</td>
+                                            </tr>
+                                            <tr>
+                                                <td><strong>自定义加密</strong></td>
+                                                <td>{$user->method}</td>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
                             </div>
                         </div>
                     </div>

+ 34 - 30
resources/views/tabler/user/invite.tpl

@@ -17,44 +17,48 @@
     </div>
     <div class="page-body">
         <div class="container-xl">
-            <div class="row row-deck">
-                <div class="col-lg-6">
-                    <div class="card">
-                        <div class="card-body">
-                            <h3 class="card-title">邀请规则</h3>
-                            <ul>
-                                <li>邀请注册的用户在账单确认后,您可获得其账单金额的 <code>{$public_setting['rebate_ratio'] * 100} %</code>
-                                    作为返利</li>
-                                <li>具体邀请返利规则请查看公告,或通过工单系统询问管理员</li>
-                                <li>部分商品的返利比例可能不遵循上面的比例</li>
-                            </ul>
-                            <p>您目前通过邀请好友获得的总返利为 <code>{$paybacks_sum}</code> 元</p>
-                        </div>
-                    </div>
-                </div>
-                <div class="col-lg-6">
-                    <div class="card">
-                        <div class="card-body">
-                            <h3 class="card-title">邀请链接</h3>
-                            {if $user->invite_num >= 0}
-                                <p>邀请链接可用次数:<code>{$user->invite_num}</code></p>
-                            {/if}
-                            <input class="form-control" value="{$invite_url}" disabled />
+            <div class="row row-cards">
+                <div class="col-12">
+                    <div class="row row-deck row-cards">
+                        <div class="col-sm-12 col-lg-6">
+                            <div class="card">
+                                <div class="card-body">
+                                    <h3 class="card-title">邀请规则</h3>
+                                    <ul>
+                                        <li>邀请注册的用户在账单确认后,您可获得其账单金额的 <code>{$public_setting['rebate_ratio'] * 100} %</code>
+                                            作为返利</li>
+                                        <li>具体邀请返利规则请查看公告,或通过工单系统询问管理员</li>
+                                        <li>部分商品的返利比例可能不遵循上面的比例</li>
+                                    </ul>
+                                    <p>您目前通过邀请好友获得的总返利为 <code>{$paybacks_sum}</code> 元</p>
+                                </div>
+                            </div>
                         </div>
-                        <div class="card-footer">
-                            <div class="d-flex">
-                                <a id="reset-url" class="btn text-red btn-link">重置</a>
-                                <a data-clipboard-text="{$invite_url}" class="copy btn btn-primary ms-auto">复制</a>
+                        <div class="col-sm-12 col-lg-6">
+                            <div class="card">
+                                <div class="card-body">
+                                    <h3 class="card-title">邀请链接</h3>
+                                    {if $user->invite_num >= 0}
+                                        <p>邀请链接可用次数:<code>{$user->invite_num}</code></p>
+                                    {/if}
+                                    <input class="form-control" value="{$invite_url}" disabled />
+                                </div>
+                                <div class="card-footer">
+                                    <div class="d-flex">
+                                        <a id="reset-url" class="btn text-red btn-link">重置</a>
+                                        <a data-clipboard-text="{$invite_url}" class="copy btn btn-primary ms-auto">复制</a>
+                                    </div>
+                                </div>
                             </div>
                         </div>
                     </div>
                 </div>
-                <div class="col-12">
-                    <div class="card my-3">
+                <div class="col-12 my-3">
+                    <div class="card">
                         <div class="card-header">
                             <h3 class="card-title">返利记录</h3>
                         </div>
-                        {if $paybacks->count() !== '0'}
+                        {if $paybacks->count() !== 0}
                             <div class="table-responsive">
                                 <table class="table card-table table-vcenter text-nowrap datatable">
                                     <thead>

+ 95 - 0
resources/views/tabler/user/money.tpl

@@ -0,0 +1,95 @@
+{include file='user/tabler_header.tpl'}
+
+<div class="page-wrapper">
+    <div class="container-xl">       
+        <div class="page-header d-print-none text-white">
+            <div class="row align-items-center">
+                <div class="col">
+                    <h2 class="page-title">
+                        <span class="home-title">余额记录</span>
+                    </h2>
+                    <div class="page-pretitle my-3">
+                        <span class="home-subtitle">查看账户余额变动记录</span>
+                    </div>
+                </div>
+                <div class="col-auto ms-auto d-print-none">
+                    <div class="btn-list">
+                        <a href="#" class="btn btn-primary d-none d-sm-inline-block" data-bs-toggle="modal"
+                           data-bs-target="#apply-giftcard-dialog">
+                            <i class="icon ti ti-cash-banknote"></i>
+                            兑换礼品卡
+                        </a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="page-body">
+        <div class="container-xl">
+            <div class="row row-deck">
+                <div class="col-sm-12 col-lg-12">
+                    <div class="card my-3">
+                        <div class="card-header">
+                            <h3 class="card-title">账户余额记录</h3>
+                        </div>
+                        {if $moneylogs->count() !== 0}
+                            <div class="table-responsive">
+                                <table class="table card-table table-vcenter text-nowrap datatable">
+                                    <thead>
+                                    <tr>
+                                        <th>事件ID</th>
+                                        <th>变动前余额</th>
+                                        <th>变动后余额</th>
+                                        <th>变动金额</th>
+                                        <th>备注</th>
+                                        <th>变动时间</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody>
+                                    {foreach $moneylogs as $moneylog}
+                                        <tr>
+                                            <td>{$moneylog->id}</td>
+                                            <td>{$moneylog->before}</td>
+                                            <td>{$moneylog->after}</td>
+                                            <td>{$moneylog->amount}</td>
+                                            <td>{$moneylog->remark}</td>
+                                            <td>{$moneylog->create_time}</td>
+                                        </tr>
+                                    {/foreach}
+                                    </tbody>
+                                </table>
+                            </div>
+                        {else}
+                            <div class="card-body">
+                                <p>没有找到记录</p>
+                            </div>
+                        {/if}
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        $("#apply-giftcard").click(function() {
+            $.ajax({
+                url: '/user/giftcard',
+                type: 'POST',
+                dataType: "json",
+                data: {
+                    giftcard: $('#giftcard').val(),
+                },
+                success: function(data) {
+                    if (data.ret === 1) {
+                        $('#success-message').text(data.msg);
+                        $('#success-dialog').modal('show');
+                    } else {
+                        $('#fail-message').text(data.msg);
+                        $('#fail-dialog').modal('show');
+                    }
+                }
+            })
+        });
+    </script>
+
+{include file='user/tabler_footer.tpl'}

+ 18 - 14
resources/views/tabler/user/tabler_header.tpl

@@ -54,9 +54,9 @@
                         </a>
                         <div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
                             {if $user->is_dark_mode}
-                            <a id="switch_theme_mode" class="dropdown-item">切换至浅色模式</a>
+                            <a id="switch_theme_mode" class="dropdown-item">浅色模式</a>
                             {else}
-                            <a id="switch_theme_mode" class="dropdown-item">切换至深色模式</a>
+                            <a id="switch_theme_mode" class="dropdown-item">深色模式</a>
                             {/if}
                             <a href="/user/logout" class="dropdown-item">登出</a>
                         </div>
@@ -104,7 +104,7 @@
                                             {/if}
                                             <a class="dropdown-item" href="/user/invite">
                                                 <i class="ti ti-friends"></i>&nbsp;
-                                                邀请注册
+                                                邀请与返利
                                             </a>
                                         </div>
                                     </div>
@@ -123,7 +123,7 @@
                                 <div class="dropdown-menu">
                                     <a class="dropdown-item" href="/user/server">
                                         <i class="ti ti-server"></i>&nbsp;
-                                        节点列表
+                                        节点
                                     </a>
                                     {if $public_setting['display_media']}
                                     <a class="dropdown-item" href="/user/media">
@@ -146,12 +146,12 @@
                                 <div class="dropdown-menu">
                                     <a class="dropdown-item" href="/user/announcement">
                                         <i class="ti ti-speakerphone"></i>&nbsp;
-                                        站点公告
+                                        公告
                                     </a>
                                     {if $public_setting['enable_ticket']}
                                     <a class="dropdown-item" href="/user/ticket">
                                         <i class="ti ti-ticket"></i>&nbsp;
-                                        工单系统
+                                        工单
                                     </a>
                                     {/if}
                                 </div>
@@ -169,12 +169,12 @@
                                 <div class="dropdown-menu">
                                     <a class="dropdown-item" href="/user/detect">
                                         <i class="ti ti-barrier-block"></i>&nbsp;
-                                        审计规则
+                                        规则
                                     </a>
                                     {if $public_setting['display_detect_log']}
                                     <a class="dropdown-item" href="/user/detect/log">
                                         <i class="ti ti-notes"></i>&nbsp;
-                                        审计日志
+                                        日志
                                     </a>
                                     {/if}
                                 </div>
@@ -193,16 +193,20 @@
                                     <div class="dropdown-menu-columns">
                                         <div class="dropdown-menu-column">
                                             <a class="dropdown-item" href="/user/product">
-                                                <i class="ti ti-building-store"></i>&nbsp;
-                                                商品列表
+                                                <i class="ti ti-list"></i>&nbsp;
+                                                商品
                                             </a>
                                             <a class="dropdown-item" href="/user/order">
                                                 <i class="ti ti-file-invoice"></i>&nbsp;
-                                                订单管理
+                                                订单
                                             </a>
                                             <a class="dropdown-item" href="/user/invoice">
-                                                <i class="ti ti-coin"></i>&nbsp;
-                                                账单管理
+                                                <i class="ti ti-file-dollar"></i>&nbsp;
+                                                账单
+                                            </a>
+                                            <a class="dropdown-item" href="/user/money">
+                                                <i class="ti ti-home-dollar"></i>&nbsp;
+                                                账户余额
                                             </a>
                                         </div>
                                     </div>
@@ -215,7 +219,7 @@
                                         <i class="ti ti-settings icon"></i>
                                     </span>
                                     <span class="nav-link-title">
-                                        站点管理
+                                        管理
                                     </span>
                                 </a>
                             </li>

+ 75 - 0
src/Controllers/User/MoneyController.php

@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Controllers\User;
+
+use App\Controllers\BaseController;
+use App\Models\GiftCard;
+use App\Models\UserMoneyLog;
+use Exception;
+use Psr\Http\Message\ResponseInterface;
+use Slim\Http\Response;
+use Slim\Http\ServerRequest;
+use voku\helper\AntiXSS;
+use function time;
+
+/**
+ *  User MoneyController
+ */
+final class MoneyController extends BaseController
+{
+    /**
+     * @throws Exception
+     */
+    public function money(ServerRequest $request, Response $response, array $args): ResponseInterface
+    {
+        $user = $this->user;
+        $moneylogs = UserMoneyLog::where('user_id', $user->id)->orderBy('id', 'desc')->get();
+
+        return $response->write(
+            $this->view()
+                ->assign('moneylogs', $moneylogs)
+                ->fetch('user/money.tpl')
+        );
+    }
+
+    public function applyGiftCard(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    {
+        $antiXss = new AntiXSS();
+        $giftcard_raw = $antiXss->xss_clean($request->getParam('giftcard'));
+
+        $giftcard = GiftCard::where('card', $giftcard_raw)->first();
+
+        if ($giftcard === null || $giftcard->status !== 0) {
+            return $response->withJson([
+                'ret' => 0,
+                'msg' => '礼品卡无效',
+            ]);
+        }
+
+        $user = $this->user;
+
+        $giftcard->status = 1;
+        $giftcard->use_time = time();
+        $giftcard->use_user = $user->id;
+        $giftcard->save();
+
+        $money_before = $user->money;
+        $user->money += $giftcard->balance;
+        $user->save();
+
+        (new UserMoneyLog())->addMoneyLog(
+            $user->id,
+            (float) $money_before,
+            (float) $user->money,
+            +$giftcard->balance,
+            '礼品卡充值 ' . $giftcard->card
+        );
+
+        return $response->withJson([
+            'ret' => 1,
+            'msg' => '充值成功',
+        ]);
+    }
+}

+ 1 - 1
src/Controllers/User/ServerController.php

@@ -20,7 +20,7 @@ final class ServerController extends BaseController
     /**
      * @throws Exception
      */
-    public function userServerPage(ServerRequest $request, Response $response, array $args): ResponseInterface
+    public function server(ServerRequest $request, Response $response, array $args): ResponseInterface
     {
         $user = $this->user;
         $query = Node::query();

+ 1 - 25
src/Controllers/UserController.php

@@ -142,7 +142,7 @@ final class UserController extends BaseController
 
             $unlock = StreamMedia::where('node_id', $node_id)
                 ->orderBy('id', 'desc')
-                ->where('created_at', '>', time() - 86460) // 只获取最近一天零一分钟内上报的数据
+                ->where('created_at', '>', time() - 86400) // 只获取最近一天内上报的数据
                 ->first();
 
             if ($unlock !== null && $node !== null) {
@@ -163,30 +163,6 @@ final class UserController extends BaseController
             }
         }
 
-        if ($_ENV['streaming_media_unlock_multiplexing'] !== null) {
-            foreach ($_ENV['streaming_media_unlock_multiplexing'] as $key => $value) {
-                $key_node = Node::where('id', $key)->first();
-                $value_node = StreamMedia::where('node_id', $value)
-                    ->orderBy('id', 'desc')
-                    ->where('created_at', '>', time() - 86460) // 只获取最近一天零一分钟内上报的数据
-                    ->first();
-
-                if ($value_node !== null) {
-                    $details = json_decode($value_node->result, true);
-                    $details = str_replace('Originals Only', '仅限自制', $details);
-                    $details = str_replace('Oversea Only', '仅限海外', $details);
-
-                    $info = [
-                        'node_name' => $key_node->name,
-                        'created_at' => $value_node->created_at,
-                        'unlock_item' => $details,
-                    ];
-
-                    $results[] = $info;
-                }
-            }
-        }
-
         $node_names = array_column($results, 'node_name');
         array_multisort($node_names, SORT_ASC, $results);