Explorar o código

feat: giftcard in /admin

Cat %!s(int64=2) %!d(string=hai) anos
pai
achega
98697b2d0c

+ 16 - 2
app/routes.php

@@ -29,17 +29,25 @@ return function (SlimApp $app): void {
         $this->get('', App\Controllers\UserController::class . ':index');
         $this->get('/', App\Controllers\UserController::class . ':index');
 
+        // 签到
         $this->post('/checkin', App\Controllers\UserController::class . ':doCheckin');
 
+        // 公告
         $this->get('/announcement', App\Controllers\UserController::class . ':announcement');
+
+        // 文档
         $this->get('/docs', App\Controllers\UserController::class . ':docs');
 
+        //流媒体解锁
         $this->get('/media', App\Controllers\UserController::class . ':media');
 
         $this->get('/profile', App\Controllers\UserController::class . ':profile');
         $this->get('/invite', App\Controllers\UserController::class . ':invite');
+
+        // 封禁
         $this->get('/banned', App\Controllers\UserController::class . ':banned');
 
+        // 节点
         $this->get('/server', App\Controllers\User\ServerController::class . ':userServerPage');
 
         // 审计
@@ -51,6 +59,7 @@ return function (SlimApp $app): void {
         $this->post('/buy', App\Controllers\User\ShopController::class . ':buy');
         $this->post('/buy_traffic_package', App\Controllers\User\ShopController::class . ':buyTrafficPackage');
 
+        // 工单
         $this->get('/ticket', App\Controllers\User\TicketController::class . ':ticket');
         $this->get('/ticket/create', App\Controllers\User\TicketController::class . ':ticketCreate');
         $this->post('/ticket', App\Controllers\User\TicketController::class . ':ticketAdd');
@@ -70,7 +79,6 @@ return function (SlimApp $app): void {
         $this->post('/mail', App\Controllers\UserController::class . ':updateMail');
         $this->post('/passwd_reset', App\Controllers\UserController::class . ':resetPasswd');
         $this->post('/method', App\Controllers\UserController::class . ':updateMethod');
-        $this->get('/trafficlog', App\Controllers\UserController::class . ':trafficLog');
         $this->get('/kill', App\Controllers\UserController::class . ':kill');
         $this->post('/kill', App\Controllers\UserController::class . ':handleKill');
         $this->get('/logout', App\Controllers\UserController::class . ':logout');
@@ -98,7 +106,7 @@ return function (SlimApp $app): void {
         // getUserAllURL
         $this->get('/getUserAllURL', App\Controllers\UserController::class . ':getUserAllURL');
 
-        //Reconstructed Payment System
+        // 支付
         $this->post('/payment/purchase/{type}', App\Services\Payment::class . ':purchase');
         $this->get('/payment/purchase/{type}', App\Services\Payment::class . ':purchase');
         $this->get('/payment/return/{type}', App\Services\Payment::class . ':returnHTML');
@@ -239,6 +247,12 @@ return function (SlimApp $app): void {
         $this->post('/setting', App\Controllers\Admin\SettingController::class . ':save');
         $this->post('/setting/email', App\Controllers\Admin\SettingController::class . ':test');
         $this->post('/setting/payment', App\Controllers\Admin\SettingController::class . ':payment');
+
+        // 礼品卡
+        $this->get('/giftcard', App\Controllers\Admin\GiftCardController::class . ':index');
+        $this->post('/giftcard', App\Controllers\Admin\GiftCardController::class . ':add');
+        $this->post('/giftcard/ajax', App\Controllers\Admin\GiftCardController::class . ':ajax');
+        $this->delete('/giftcard/{id}', App\Controllers\Admin\GiftCardController::class . ':delete');
     })->add(new Admin());
 
     if ($_ENV['enableAdminApi']) {

+ 0 - 19
config/appprofile.example.php

@@ -212,25 +212,6 @@ $_ENV['Clash_Config'] = [
         'DOMAIN,bdtj.tagtic.cn,🎯 全球直连',
         'DOMAIN,log.mmstat.com,🎯 全球直连',
         'DOMAIN,sycm.mmstat.com,🎯 全球直连',
-        'DOMAIN-SUFFIX,blog.google,🎯 全球直连',
-        'DOMAIN-SUFFIX,googletraveladservices.com,🎯 全球直连',
-        'DOMAIN,clientservices.googleapis.com,🎯 全球直连',
-        'DOMAIN,dl.google.com,🎯 全球直连',
-        'DOMAIN,dl.l.google.com,🎯 全球直连',
-        'DOMAIN,update.googleapis.com,🎯 全球直连',
-        'DOMAIN,translate.googleapis.com,🎯 全球直连',
-        'DOMAIN,fonts.googleapis.com,🎯 全球直连',
-        'DOMAIN,fonts.gstatic.com,🎯 全球直连',
-        'DOMAIN,mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt1-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt2-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt3-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt4-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt5-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt6-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt7-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,alt8-mtalk.google.com,🎯 全球直连',
-        'DOMAIN,pubads.g.doubleclick.net,🎯 全球直连',
         'DOMAIN,fairplay.l.qq.com,🎯 全球直连',
         'DOMAIN,livew.l.qq.com,🎯 全球直连',
         'DOMAIN,vd.l.qq.com,🎯 全球直连',

+ 0 - 40
config/settings.json

@@ -899,46 +899,6 @@
         "default": "chacha20-ietf",
         "mark": "默认加密"
     },
-    {
-        "id": null,
-        "item": "sign_up_for_protocol",
-        "value": "auth_aes128_sha1",
-        "class": "register",
-        "is_public": 0,
-        "type": "string",
-        "default": "auth_aes128_sha1",
-        "mark": "默认协议"
-    },
-    {
-        "id": null,
-        "item": "sign_up_for_obfs",
-        "value": "http_simple",
-        "class": "register",
-        "is_public": 0,
-        "type": "string",
-        "default": "http_simple",
-        "mark": "默认混淆"
-    },
-    {
-        "id": null,
-        "item": "sign_up_for_protocol_param",
-        "value": "",
-        "class": "register",
-        "is_public": 0,
-        "type": "string",
-        "default": "",
-        "mark": "默认协议参数"
-    },
-    {
-        "id": null,
-        "item": "sign_up_for_obfs_param",
-        "value": "www.jd.hk",
-        "class": "register",
-        "is_public": 0,
-        "type": "string",
-        "default": "www.jd.hk",
-        "mark": "默认混淆参数"
-    },
     {
         "id": null,
         "item": "mu_suffix",

+ 12 - 29
db/migrations/20000101000000_init_database.php.new

@@ -79,7 +79,7 @@ final class InitDatabase extends AbstractMigration
             ->addColumn('sort', 'integer', [])
             ->addColumn('traffic_rate', 'float', [ 'default' => 1 ])
             ->addColumn('node_class', 'integer', [ 'default' => 0 ])
-            ->addColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'null' => false ])
+            ->addColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'default' => 0, 'null' => false ])
             ->addColumn('node_connector', 'integer', [ 'default' => 0 ])
             ->addColumn('node_bandwidth', 'biginteger', [ 'default' => 0 ])
             ->addColumn('node_bandwidth_limit', 'biginteger', [ 'default' => 0 ])
@@ -321,38 +321,21 @@ final class InitDatabase extends AbstractMigration
             ->addIndex([ 'user_id' ])
             ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
             ->create();
-            ->addColumn('status', 'text', [ 'comment' => '订单状态' ])
+
+        $this->table('gift_card', [ 'id' => false, 'primary_key' => [ 'id' ]])
+            ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
+            ->addColumn('card', 'text', ['comment' => '卡号'])
+            ->addColumn('balance', 'integer', ['comment' => '余额'])
+            ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
+            ->addColumn('status', 'integer', ['comment' => '使用状态'])
+            ->addColumn('use_time', 'integer', ['comment' => '使用时间'])
+            ->addColumn('use_user', 'integer', ['comment' => '使用用户'])
+            ->addIndex([ 'id' ])
             ->addIndex([ 'status' ])
+            ->create();
     }
 
     public function down(): void
     {
-        $this->table('user')->drop()->update();
-        $this->table('node')->drop()->update();
-        $this->table('alive_ip')->drop()->update();
-        $this->table('announcement')->drop()->update();
-        $this->table('bought')->drop()->update();
-        $this->table('code')->drop()->update();
-        $this->table('config')->drop()->update();
-        $this->table('coupon')->drop()->update();
-        $this->table('detect_ban_log')->drop()->update();
-        $this->table('detect_list')->drop()->update();
-        $this->table('detect_log')->drop()->update();
-        $this->table('email_queue')->drop()->update();
-        $this->table('email_verify')->drop()->update();
-        $this->table('gconfig')->drop()->update();
-        $this->table('link')->drop()->update();
-        $this->table('login_ip')->drop()->update();
-        $this->table('payback')->drop()->update();
-        $this->table('paylist')->drop()->update();
-        $this->table('shop')->drop()->update();
-        $this->table('user_invite_code')->drop()->update();
-        $this->table('node_online_log')->drop()->update();
-        $this->table('user_password_reset')->drop()->update();
-        $this->table('telegram_session')->drop()->update();
-        $this->table('ticket')->drop()->update();
-        $this->table('user_hourly_usage')->drop()->update();
-        $this->table('user_subscribe_log')->drop()->update();
-        $this->table('user_token')->drop()->update();
     }
 }

+ 1 - 1
db/migrations/20221205162200_change_number_type.php

@@ -17,7 +17,7 @@ final class ChangeNumberType extends AbstractMigration
             ->changeColumn('uuid', 'uuid', [ 'comment' => 'UUID', 'null' => false ])
             ->save();
         $this->table('node')
-            ->changeColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'null' => false ])
+            ->changeColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'default' => 0, 'null' => false ])
             ->save();
     }
 

+ 30 - 0
db/migrations/20221211230900_gift_card_table.php

@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+use Phinx\Migration\AbstractMigration;
+
+final class GiftCardTable extends AbstractMigration
+{
+    public function up(): void
+    {
+        if (! $this->hasTable('gift_card')) {
+            $this->table('gift_card', [ 'id' => false, 'primary_key' => [ 'id' ]])
+                ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
+                ->addColumn('card', 'text', ['comment' => '卡号'])
+                ->addColumn('balance', 'integer', ['comment' => '余额'])
+                ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
+                ->addColumn('status', 'integer', ['comment' => '使用状态'])
+                ->addColumn('use_time', 'integer', ['comment' => '使用时间'])
+                ->addColumn('use_user', 'integer', ['comment' => '使用用户'])
+                ->addIndex([ 'id' ])
+                ->addIndex([ 'status' ])
+                ->create();
+        }
+    }
+
+    public function down(): void
+    {
+        $this->table('gift_card')->drop()->update();
+    }
+}

+ 19 - 0
db/migrations/20221212141700_fix_node_default_value.php

@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+use Phinx\Migration\AbstractMigration;
+
+final class FixNodeDefaultValue extends AbstractMigration
+{
+    public function up(): void
+    {
+        $this->table('node')
+            ->changeColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'default' => 0, 'null' => false ])
+            ->save();
+    }
+
+    public function down(): void
+    {
+    }
+}

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

@@ -0,0 +1,209 @@
+{include file='admin/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 my-3">礼品卡</span>
+                    </h2>
+                    <div class="page-pretitle">
+                        <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="#create-dialog">
+                            <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>
+        </div>
+    </div>
+    <div class="page-body">
+        <div class="container-xl">
+            <div class="row row-deck row-cards">
+                <div class="col-12">
+                    <div class="card">
+                        <div class="table-responsive">
+                            <table id="data_table" class="table card-table table-vcenter text-nowrap datatable">
+                                <thead>
+                                    <tr>
+                                        {foreach $details['field'] as $key => $value}
+                                            <th>{$value}</th>
+                                        {/foreach}
+                                    </tr>
+                                </thead>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal modal-blur fade" id="create-dialog" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">礼品卡内容</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                </div>
+                <div class="modal-body">
+                    {foreach $details['create_dialog'] as $detail}
+                        {if $detail['type'] == 'input'}
+                            <div class="form-group mb-3 row">
+                                <label class="form-label col-3 col-form-label">{$detail['info']}</label>
+                                <div class="col">
+                                    <input id="{$detail['id']}" type="text" class="form-control"
+                                        placeholder="{$detail['placeholder']}">
+                                </div>
+                            </div>
+                        {/if}
+                        {if $detail['type'] == 'textarea'}
+                            <div class="form-group mb-3 row">
+                                <label class="form-label col-3 col-form-label">{$detail['info']}</label>
+                                <textarea id="{$detail['id']}" class="col form-control" rows="{$detail['rows']}"
+                                    placeholder="{$detail['placeholder']}"></textarea>
+                            </div>
+                        {/if}
+                        {if $detail['type'] == 'select'}
+                            <div class="form-group mb-3 row">
+                                <label class="form-label col-3 col-form-label">{$detail['info']}</label>
+                                <div class="col">
+                                    <select id="{$detail['id']}" class="col form-select">
+                                        {foreach $detail['select'] as $key => $value}
+                                            <option value="{$key}">{$value}</option>
+                                        {/foreach}
+                                    </select>
+                                </div>
+                            </div>
+                        {/if}
+                    {/foreach}
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn me-auto" data-bs-dismiss="modal">取消</button>
+                    <button id="create-button" onclick="createGiftCard()"
+                        type="button" class="btn btn-primary" data-bs-dismiss="modal">创建</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        var table = $('#data_table').DataTable({
+            ajax: {
+                url: '/admin/giftcard/ajax',
+                type: 'POST',
+                dataSrc: 'giftcards'
+            },
+            "autoWidth":false,
+            'iDisplayLength': 10,
+            'scrollX': true,
+            'order': [
+                [1, 'desc']
+            ],
+            columns: [
+                {foreach $details['field'] as $key => $value}
+                { data: '{$key}' },
+                {/foreach}
+            ],
+            "columnDefs":[
+                { targets:[0],orderable:false }
+            ],
+            "dom": "<'row px-3 py-3'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
+                "<'row'<'col-sm-12'tr>>" +
+                "<'row card-footer d-flex d-flexalign-items-center'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
+            language: {
+                "sProcessing": "处理中...",
+                "sLengthMenu": "显示 _MENU_ 条",
+                "sZeroRecords": "没有匹配结果",
+                "sInfo": "第 _START_ 至 _END_ 项结果,共 _TOTAL_项",
+                "sInfoEmpty": "第 0 至 0 项结果,共 0 项",
+                "sInfoFiltered": "(在 _MAX_ 项中查找)",
+                "sInfoPostFix": "",
+                "sSearch": "<i class=\"ti ti-search\"></i> ",
+                "sUrl": "",
+                "sEmptyTable": "表中数据为空",
+                "sLoadingRecords": "载入中...",
+                "sInfoThousands": ",",
+                "oPaginate": {
+                    "sFirst": "首页",
+                    "sPrevious": "<i class=\"titi-arrow-left\"></i>",
+                    "sNext": "<i class=\"ti ti-arrow-right\"><i>",
+                    "sLast": "末页"
+                },
+                "oAria": {
+                    "sSortAscending": ": 以升序排列此列",
+                    "sSortDescending": ": 以降序排列此列"
+                }
+            },
+        });
+
+        function loadTable() {
+            table;
+        }
+
+        function createGiftCard() {
+            $.ajax({
+                url: '/admin/giftcard',
+                type: 'POST',
+                dataType: "json",
+                data: {
+                    {foreach $details['create_dialog'] as $detail}
+                        {$detail['id']}: $('#{$detail['id']}').val(),
+                    {/foreach}
+                },
+                success: function(data) {
+                    if (data.ret == 1) {
+                        $('#success-message').text(data.msg);
+                        $('#success-dialog').modal('show');
+                        reloadTableAjax();
+                    } else {
+                        $('#fail-message').text(data.msg);
+                        $('#fail-dialog').modal('show');
+                    }
+                }
+            })
+        };
+
+        function deleteGiftCard(giftcard_id) {
+            $('#notice-message').text('确定删除此礼品卡?');
+            $('#notice-dialog').modal('show');
+            $('#notice-confirm').on('click', function() {
+                $.ajax({
+                    url: "/admin/giftcard/" + giftcard_id,
+                    type: 'DELETE',
+                    dataType: "json",
+                    success: function(data) {
+                        if (data.ret == 1) {
+                            $('#success-message').text(data.msg);
+                            $('#success-dialog').modal('show');
+                            reloadTableAjax();
+                        } else {
+                            $('#fail-message').text(data.msg);
+                            $('#fail-dialog').modal('show');
+                        }
+                    }
+                })
+            });
+        };
+
+        function reloadTableAjax() {
+            table.ajax.reload(null, false);
+        }
+
+        loadTable();
+    </script>
+
+{include file='admin/tabler_footer.tpl'}

+ 1 - 2
resources/views/tabler/admin/node/create.tpl

@@ -67,7 +67,6 @@
                                         <option value="11">V2Ray</option>
                                         <option value="14">Trojan</option>
                                         <option value="0">Shadowsocks</option>
-                                        <option value="1">ShadowsocksR</option>
                                     </select>
                                 </div>
                             </div>
@@ -161,7 +160,7 @@
 
     $("#create-node").click(function() {
         $.ajax({
-            url: '/node',
+            url: '/admin/node',
             type: 'POST',
             dataType: "json",
             data: {

+ 0 - 1
resources/views/tabler/admin/node/edit.tpl

@@ -67,7 +67,6 @@
                                         <option value="14" {if $node->sort === 14}selected{/if}>Trojan</option>
                                         <option value="11" {if $node->sort === 11}selected{/if}>V2Ray</option>
                                         <option value="0" {if $node->sort === 0}selected{/if}>Shadowsocks</option>
-                                        <option value="1" {if $node->sort === 1}selected{/if}>ShadowsocksR</option>
                                     </select>
                                 </div>
                             </div>

+ 0 - 24
resources/views/tabler/admin/setting.tpl

@@ -778,26 +778,6 @@
                                             <label class="floating-label">默认加密</label>
                                             <input class="form-control maxwidth-edit" id="sign_up_for_method" value="{$settings['sign_up_for_method']}">
                                         </div>
-                                        <!-- sign_up_for_protocol -->
-                                        <div class="form-group form-group-label">
-                                            <label class="floating-label">默认协议</label>
-                                            <input class="form-control maxwidth-edit" id="sign_up_for_protocol" value="{$settings['sign_up_for_protocol']}">
-                                        </div>
-                                        <!-- sign_up_for_protocol_param -->
-                                        <div class="form-group form-group-label">
-                                            <label class="floating-label">默认协议参数</label>
-                                            <input class="form-control maxwidth-edit" id="sign_up_for_protocol_param" value="{$settings['sign_up_for_protocol_param']}">
-                                        </div>
-                                        <!-- sign_up_for_obfs -->
-                                        <div class="form-group form-group-label">
-                                            <label class="floating-label">默认混淆</label>
-                                            <input class="form-control maxwidth-edit" id="sign_up_for_obfs" value="{$settings['sign_up_for_obfs']}">
-                                        </div>
-                                        <!-- sign_up_for_obfs_param -->
-                                        <div class="form-group form-group-label">
-                                            <label class="floating-label">默认混淆参数</label>
-                                            <input class="form-control maxwidth-edit" id="sign_up_for_obfs_param" value="{$settings['sign_up_for_obfs_param']}">
-                                        </div>
                                         <!-- mu_suffix -->
                                         <div class="form-group form-group-label">
                                             <label class="floating-label">单端口多用户混淆参数后缀</label>
@@ -1726,10 +1706,6 @@
                     connection_device_limit: $$getValue('connection_device_limit'),
                     connection_rate_limit: $$getValue('connection_rate_limit'),
                     sign_up_for_method: $$getValue('sign_up_for_method'),
-                    sign_up_for_protocol: $$getValue('sign_up_for_protocol'),
-                    sign_up_for_protocol_param: $$getValue('sign_up_for_protocol_param'),
-                    sign_up_for_obfs: $$getValue('sign_up_for_obfs'),
-                    sign_up_for_obfs_param: $$getValue('sign_up_for_obfs_param'),
                     mu_suffix: $$getValue('mu_suffix'),
                     mu_regex: $$getValue('mu_regex'),
                     reg_forbidden_ip: $$getValue('reg_forbidden_ip'),

+ 12 - 41
resources/views/tabler/admin/tabler_header.tpl

@@ -101,10 +101,6 @@
                                                 <i class="ti ti-server-2"></i>&nbsp;
                                                 节点
                                             </a>
-                                            <a class="dropdown-item" href="/admin/giftcard">
-                                                <i class="ti ti-gift"></i>&nbsp;
-                                                礼品卡
-                                            </a>
                                         </div>
                                     </div>
                                 </div>
@@ -130,39 +126,6 @@
                                     </a>
                                 </div>
                             </li>
-                            <li class="nav-item dropdown">
-                                <a class="nav-link dropdown-toggle" href="#navbar-extra" data-bs-toggle="dropdown"
-                                    data-bs-auto-close="outside" role="button" aria-expanded="false">
-                                    <span class="nav-link-icon d-md-none d-lg-inline-block">
-                                        <i class="ti ti-report-analytics icon"></i>
-                                    </span>
-                                    <span class="nav-link-title">
-                                        报表
-                                    </span>
-                                </a>
-                                <div class="dropdown-menu">
-                                    <a class="dropdown-item"
-                                        href="/admin/chart/index">
-                                        <i class="ti ti-timeline"></i>&nbsp;
-                                        总览
-                                    </a>
-                                    <a class="dropdown-item"
-                                        href="/admin/chart/finance">
-                                        <i class="ti ti-businessplan"></i>&nbsp;
-                                        财务
-                                    </a>
-                                    <a class="dropdown-item"
-                                        href="/admin/chart/user/{date("Ymd", strtotime("-1 day"))}">
-                                        <i class="ti ti-sort-ascending-2"></i>&nbsp;
-                                        用户流量
-                                    </a>
-                                    <a class="dropdown-item"
-                                        href="/admin/chart/node/{date("Ymd", strtotime("-1 day"))}">
-                                        <i class="ti ti-sort-descending-2"></i>&nbsp;
-                                        节点流量
-                                    </a>
-                                </div>
-                            </li>
                             <li class="nav-item dropdown">
                                 <a class="nav-link dropdown-toggle" href="#navbar-extra" data-bs-toggle="dropdown"
                                     data-bs-auto-close="outside" role="button" aria-expanded="false">
@@ -192,7 +155,7 @@
                                     </a>
                                     <a class="dropdown-item" href="/admin/trafficlog">
                                         <i class="ti ti-arrows-up-down"></i>&nbsp;
-                                        流量记录
+                                        流量使用
                                     </a>
                                 </div>
                             </li>
@@ -221,10 +184,10 @@
                                 <a class="nav-link dropdown-toggle" href="#navbar-layout" data-bs-toggle="dropdown"
                                     data-bs-auto-close="outside" role="button" aria-expanded="false">
                                     <span class="nav-link-icon d-md-none d-lg-inline-block">
-                                        <i class="ti ti-building-store icon"></i>
+                                        <i class="ti ti-coin icon"></i>
                                     </span>
                                     <span class="nav-link-title">
-                                        商品
+                                        财务
                                     </span>
                                 </a>
                                 <div class="dropdown-menu">
@@ -232,16 +195,24 @@
                                         <div class="dropdown-menu-column">
                                             <a class="dropdown-item" href="/admin/product">
                                                 <i class="ti ti-list-details"></i>&nbsp;
-                                                列表
+                                                商品
                                             </a>
                                             <a class="dropdown-item" href="/admin/order">
                                                 <i class="ti ti-receipt"></i>&nbsp;
                                                 订单
                                             </a>
+                                            <a class="dropdown-item" href="/admin/invoice">
+                                                <i class="ti ti-file-dollar"></i>&nbsp;
+                                                账单
+                                            </a>
                                             <a class="dropdown-item" href="/admin/coupon">
                                                 <i class="ti ti-ticket"></i>&nbsp;
                                                 优惠码
                                             </a>
+                                            <a class="dropdown-item" href="/admin/giftcard">
+                                                <i class="ti ti-gift"></i>&nbsp;
+                                                礼品卡
+                                            </a>
                                         </div>
                                     </div>
                                 </div>

+ 0 - 37
resources/views/tabler/admin/user/edit.tpl

@@ -235,43 +235,6 @@
                                     <input id="method" type="text" class="form-control" value="{$edit_user->method}">
                                 </div>
                             </div>
-                            <div class="form-group mb-3 row">
-                                <label class="form-label col-3 col-form-label">协议</label>
-                                <div class="col">
-                                    <input id="protocol" type="text" class="form-control"
-                                        value="{$edit_user->protocol}">
-                                </div>
-                            </div>
-                            <div class="form-group mb-3 row">
-                                <label class="form-label col-3 col-form-label">协议参数</label>
-                                <div class="col">
-                                    <input id="protocol_param" type="text" class="form-control"
-                                        value="{$edit_user->protocol_param}">
-                                </div>
-                            </div>
-                            <div class="form-group mb-3 row">
-                                <label class="form-label col-3 col-form-label">混淆方式</label>
-                                <div class="col">
-                                    <input id="obfs" type="text" class="form-control" value="{$edit_user->obfs}">
-                                </div>
-                            </div>
-                            <div class="form-group mb-3 row">
-                                <label class="form-label col-3 col-form-label">混淆参数</label>
-                                <div class="col">
-                                    <input id="obfs_param" type="text" class="form-control"
-                                        value="{$edit_user->obfs_param}">
-                                </div>
-                            </div>
-                            <div class="form-group mb-3 row">
-                                <label class="form-label col-3 col-form-label">单端口多用户承载端口</label>
-                                <div class="col">
-                                    <select id="is_multi_user" class="col form-select">
-                                        <option value="0">非单端口多用户承载端口</option>
-                                        <option value="1">混淆式单端口多用户承载端口</option>
-                                        <option value="2">协议式单端口多用户承载端口</option>
-                                    </select>
-                                </div>
-                            </div>
                             <div class="hr-text">
                                 <span>访问限制</span>
                             </div>

+ 126 - 0
src/Controllers/Admin/GiftCardController.php

@@ -0,0 +1,126 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Controllers\Admin;
+
+use App\Controllers\BaseController;
+use App\Models\GiftCard;
+use App\Utils\Tools;
+
+final class GiftCardController extends BaseController
+{
+    public static $details = [
+        'field' => [
+            'op' => '操作',
+            'id' => '礼品卡ID',
+            'card' => '卡号',
+            'balance' => '面值',
+            'create_time' => '创建时间',
+            'status' => '使用状态',
+            'use_time' => '使用时间',
+            'use_user' => '使用用户',
+        ],
+        'create_dialog' => [
+            [
+                'id' => 'card_number',
+                'info' => '创建数量',
+                'type' => 'input',
+                'placeholder' => '',
+            ],
+            [
+                'id' => 'card_value',
+                'info' => '礼品卡面值',
+                'type' => 'input',
+                'placeholder' => '',
+            ],
+            [
+                'id' => 'card_length',
+                'info' => '礼品卡长度',
+                'type' => 'select',
+                'select' => [
+                    '12' => '12位',
+                    '18' => '18位',
+                    '24' => '24位',
+                    '30' => '30位',
+                    '36' => '36位',
+                ],
+            ],
+        ],
+    ];
+
+    public function index($request, $response, $args)
+    {
+        return $response->write(
+            $this->view()
+                ->assign('details', self::$details)
+                ->display('admin/giftcard.tpl')
+        );
+    }
+
+    public function add($request, $response, $args)
+    {
+        $card_number = $request->getParam('card_number');
+        $card_value = $request->getParam('card_value');
+        $card_length = $request->getParam('card_length');
+        $card_added = '';
+
+        if ($card_number === null || $card_number < 0) {
+            return $response->withJson([
+                'ret' => 0,
+                'msg' => '生成数量不能为空或小于0',
+            ]);
+        }
+
+        if ($card_value === null || $card_value < 0) {
+            return $response->withJson([
+                'ret' => 0,
+                'msg' => '礼品卡面值不能为空或小于0',
+            ]);
+        }
+
+        for ($i = 0; $i < $card_number; $i++) {
+            $card = Tools::genRandomChar($card_length);
+            // save to database
+            $giftcard = new GiftCard();
+            $giftcard->card = $card;
+            $giftcard->balance = $card_value;
+            $giftcard->create_time = \time();
+            $giftcard->status = 0;
+            $giftcard->use_time = 0;
+            $giftcard->use_user = 0;
+            $giftcard->save();
+            $card_added .= $card . PHP_EOL;
+        }
+
+        return $response->withJson([
+            'ret' => 1,
+            'msg' => '添加成功' . PHP_EOL . $card_added,
+        ]);
+    }
+
+    public function ajax($request, $response, $args)
+    {
+        $giftcards = GiftCard::orderBy('id', 'desc')->get();
+        foreach ($giftcards as $giftcard) {
+            $giftcard->op = '<button type="button" class="btn btn-red" id="delete-gift-card-' . $giftcard->id . '" 
+        onclick="deleteGiftCard(' . $giftcard->id . ')">删除</button>';
+            $giftcard->status = Tools::getGiftCardStatus($giftcard);
+            $giftcard->create_time = Tools::toDateTime((int) $giftcard->create_time);
+            $giftcard->use_time = Tools::toDateTime((int) $giftcard->use_time);
+        }
+        return $response->withJson([
+            'giftcards' => $giftcards,
+        ]);
+    }
+
+    public function delete($request, $response, $args)
+    {
+        $card_id = $args['id'];
+        GiftCard::find($card_id)->delete();
+        return $response->withJson([
+            'ret' => 1,
+            'msg' => '删除成功',
+        ]);
+    }
+}

+ 7 - 1
src/Controllers/Admin/NodeController.php

@@ -119,7 +119,13 @@ final class NodeController extends BaseController
         }
 
         $node->node_class = $request->getParam('node_class');
-        $node->node_bandwidth_limit = $request->getParam('node_bandwidth_limit') * 1024 * 1024 * 1024;
+
+        if ($request->getParam('node_bandwidth_limit') === null || $request->getParam('node_bandwidth_limit') === '') {
+            $node->node_bandwidth_limit = 0;
+        } else {
+            $node->node_bandwidth_limit = $request->getParam('node_bandwidth_limit') * 1024 * 1024 * 1024;
+        }
+
         $node->bandwidthlimit_resetday = $request->getParam('bandwidthlimit_resetday');
         $node->password = Tools::genRandomChar(32);
 

+ 1 - 1
src/Controllers/Admin/SettingController.php

@@ -96,7 +96,7 @@ final class SettingController extends BaseController
                 break;
                 // 注册设置
             case 'register':
-                $list = ['reg_mode', 'reg_email_verify', 'email_verify_ttl', 'email_verify_ip_limit', 'random_group', 'min_port', 'max_port',   'sign_up_for_free_traffic','free_user_reset_day', 'free_user_reset_bandwidth', 'sign_up_for_free_time', 'sign_up_for_class', 'sign_up_for_class_time', 'sign_up_for_invitation_codes', 'connection_device_limit', 'connection_rate_limit', 'sign_up_for_method', 'sign_up_for_protocol', 'sign_up_for_protocol_param', 'sign_up_for_obfs', 'sign_up_for_obfs_param', 'mu_suffix', 'mu_regex', 'reg_forbidden_ip', 'reg_forbidden_port', 'enable_reg_im', 'sign_up_for_daily_report'];
+                $list = ['reg_mode', 'reg_email_verify', 'email_verify_ttl', 'email_verify_ip_limit', 'random_group', 'min_port', 'max_port',   'sign_up_for_free_traffic','free_user_reset_day', 'free_user_reset_bandwidth', 'sign_up_for_free_time', 'sign_up_for_class', 'sign_up_for_class_time', 'sign_up_for_invitation_codes', 'connection_device_limit', 'connection_rate_limit', 'sign_up_for_method', 'mu_suffix', 'mu_regex', 'reg_forbidden_ip', 'reg_forbidden_port', 'enable_reg_im', 'sign_up_for_daily_report'];
                 break;
                 // 邀请设置
             case 'invite':

+ 0 - 10
src/Controllers/Admin/UserController.php

@@ -68,7 +68,6 @@ final class UserController extends BaseController
         'remark',
         'pass',
         'money',
-        'is_multi_user',
         'is_admin',
         'is_banned',
         'banned_reason',
@@ -87,10 +86,6 @@ final class UserController extends BaseController
         'port',
         'passwd',
         'method',
-        'protocol',
-        'protocol_param',
-        'obfs',
-        'obfs_param',
         'forbidden_ip',
         'forbidden_port',
     ];
@@ -204,7 +199,6 @@ final class UserController extends BaseController
         $user->user_name = $request->getParam('user_name');
         $user->remark = $request->getParam('remark');
         $user->money = $request->getParam('money');
-        $user->is_multi_user = $request->getParam('is_multi_user');
         $user->is_admin = $request->getParam('is_admin') === 'true' ? 1 : 0;
         $user->is_banned = $request->getParam('is_banned') === 'true' ? 1 : 0;
         $user->banned_reason = $request->getParam('banned_reason');
@@ -223,10 +217,6 @@ final class UserController extends BaseController
         $user->port = $request->getParam('port');
         $user->passwd = $request->getParam('passwd');
         $user->method = $request->getParam('method');
-        $user->protocol = $request->getParam('protocol');
-        $user->protocol_param = $request->getParam('protocol_param');
-        $user->obfs = $request->getParam('obfs');
-        $user->obfs_param = $request->getParam('obfs_param');
         $user->forbidden_ip = str_replace(PHP_EOL, ',', $request->getParam('forbidden_ip'));
         $user->forbidden_port = str_replace(PHP_EOL, ',', $request->getParam('forbidden_port'));
 

+ 0 - 4
src/Controllers/AuthController.php

@@ -288,10 +288,6 @@ final class AuthController extends BaseController
         $user->u = 0;
         $user->d = 0;
         $user->method = $configs['sign_up_for_method'];
-        $user->protocol = $configs['sign_up_for_protocol'];
-        $user->protocol_param = $configs['sign_up_for_protocol_param'];
-        $user->obfs = $configs['sign_up_for_obfs'];
-        $user->obfs_param = $configs['sign_up_for_obfs_param'];
         $user->forbidden_ip = Setting::obtain('reg_forbidden_ip');
         $user->forbidden_port = Setting::obtain('reg_forbidden_port');
         $user->im_type = $imtype;

+ 11 - 0
src/Models/GiftCard.php

@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Models;
+
+final class GiftCard extends Model
+{
+    protected $connection = 'default';
+    protected $table = 'gift_card';
+}

+ 8 - 0
src/Utils/Tools.php

@@ -512,4 +512,12 @@ final class Tools
         }
         return $sort;
     }
+
+    /**
+     * 礼品卡状态
+     */
+    public static function getGiftCardStatus($giftcard)
+    {
+        return $giftcard->status ? '已使用' : '未使用';
+    }
 }