Browse Source

thinkphp6项目初始化(基于danjiwanjia_api)

markc1d 2 months ago
commit
27ca824ecd
100 changed files with 7601 additions and 0 deletions
  1. 5 0
      .gitignore
  2. 1 0
      .htaccess
  3. 42 0
      .travis.yml
  4. 26 0
      404.html
  5. 32 0
      LICENSE.txt
  6. 56 0
      README.md
  7. 1 0
      app/.htaccess
  8. 164 0
      app/AdminBaseController.php
  9. 22 0
      app/AppService.php
  10. 228 0
      app/BaseController.php
  11. 58 0
      app/ExceptionHandle.php
  12. 8 0
      app/Request.php
  13. 2 0
      app/common.php
  14. 1691 0
      app/controller/Admin.php
  15. 100 0
      app/controller/Authorize.php
  16. 151 0
      app/controller/Common.php
  17. 101 0
      app/controller/Email.php
  18. 71 0
      app/controller/Feedback.php
  19. 416 0
      app/controller/Forum.php
  20. 141 0
      app/controller/Friend.php
  21. 143 0
      app/controller/Fuzhu.php
  22. 316 0
      app/controller/Game.php
  23. 20 0
      app/controller/Kefu.php
  24. 106 0
      app/controller/Message.php
  25. 246 0
      app/controller/Pay.php
  26. 141 0
      app/controller/Payment.php
  27. 304 0
      app/controller/User.php
  28. 210 0
      app/controller/Vip.php
  29. 17 0
      app/event.php
  30. 12 0
      app/middleware.php
  31. 9 0
      app/provider.php
  32. 9 0
      app/service.php
  33. 54 0
      composer.json
  34. 1522 0
      composer.lock
  35. 32 0
      config/app.php
  36. 29 0
      config/cache.php
  37. 43 0
      config/captcha.php
  38. 9 0
      config/console.php
  39. 20 0
      config/cookie.php
  40. 63 0
      config/database.php
  41. 24 0
      config/filesystem.php
  42. 27 0
      config/lang.php
  43. 45 0
      config/log.php
  44. 8 0
      config/middleware.php
  45. 29 0
      config/phpu_captcha.php
  46. 45 0
      config/route.php
  47. 19 0
      config/session.php
  48. 10 0
      config/trace.php
  49. 25 0
      config/view.php
  50. 2 0
      extend/.gitignore
  51. 39 0
      index.html
  52. 563 0
      mysql/thinkphp6_template.sql
  53. 8 0
      public/.htaccess
  54. 1 0
      public/.user.ini
  55. 1 0
      public/.well-known/acme-challenge/fm7DNGpS5Fo2HnpsNXujRd7W9laiKAZpqzPbifJNkRQ
  56. 81 0
      public/aa.html
  57. BIN
      public/favicon.ico
  58. 30 0
      public/index.php
  59. 2 0
      public/robots.txt
  60. 19 0
      public/router.php
  61. 2 0
      public/static/.gitignore
  62. BIN
      public/storage/attachments/20250828/18e86ee37f9ee18a6034d5f2d0a80f7e.zip
  63. BIN
      public/storage/attachments/20250828/210f5d2ca5c973a225edfbd979e91cf7.zip
  64. BIN
      public/storage/attachments/20250828/29034430d609b0d7a97a57e51305d715.zip
  65. BIN
      public/storage/attachments/20250828/37dc4d9c49de2aaa63c8829fadda5179.zip
  66. BIN
      public/storage/attachments/20250828/381621b994066abca8214a7aa2c59674.zip
  67. BIN
      public/storage/attachments/20250828/540ebbc8e311a74efdc02b410e26be4c.zip
  68. BIN
      public/storage/attachments/20250828/5a5ed68bf0d80e6ee32ae5b1ac7d1604.zip
  69. BIN
      public/storage/attachments/20250828/9f919cf4bccae5567d573cb8d2adb22d.zip
  70. BIN
      public/storage/attachments/20250828/a1c628104980044fb90b784f5e1e88c4.zip
  71. BIN
      public/storage/attachments/20250828/a97239d9cc55597e04d44a225e56cd04.zip
  72. BIN
      public/storage/attachments/20250828/bdd4afac1ad873d7b40065c3a674848b.zip
  73. BIN
      public/storage/attachments/20250828/dbe539f238e3d7b1dcfb9d9b93035434.zip
  74. BIN
      public/storage/attachments/20250828/dc1bd82443efe20e276b7fc51934498a.zip
  75. BIN
      public/storage/avatar/20250812/3e4d666f44ece1335ce98633a38c8df1.png
  76. BIN
      public/storage/avatar/20250813/c87b94bdfa34a13e9f121625111a8a38.png
  77. BIN
      public/storage/avatar/20250813/ca1400dc7de4ae08a5923e698111dfbf.png
  78. BIN
      public/storage/avatar/20250813/dabbe83db838841387684f3a0064a281.png
  79. BIN
      public/storage/avatar/20250909/4fecbdf5190363f3f1d6204caab0aefb.png
  80. BIN
      public/storage/images/20250826/3eea75d4856edc1005d76932f2d33742.png
  81. BIN
      public/storage/images/20250826/50097c4598e51f5840a65d0b2c11ec3e.png
  82. BIN
      public/storage/images/20250826/5af796bfc73024eed5ccf7bc778a991d.png
  83. BIN
      public/storage/images/20250826/e144336c4665d32b6993e0d3852a4cb6.png
  84. BIN
      public/storage/images/20250828/44aa7ab18ec5713b4fb843d90376b173.png
  85. BIN
      public/storage/images/20250828/630fff6b8b2eb73a2e0bc07248f76615.png
  86. BIN
      public/storage/images/20250828/be278a6849f12adc73c00dca24732a00.png
  87. BIN
      public/storage/images/20250828/c19fd410b35c32c29b7f555cbe9266a3.png
  88. BIN
      public/storage/images/20250828/e3c65039aef4c0a49c9a180d050ea40e.png
  89. BIN
      public/storage/images/20250828/f4bc03a6f519f6519f65405cbd87df86.png
  90. BIN
      public/storage/images/20250903/0806ec92f46220f5e05f0923ed82285c.png
  91. BIN
      public/storage/images/20250903/182a451982f7c6595052b4e06791678b.png
  92. BIN
      public/storage/images/20250903/39c90e35c7d53132053fdc6f66e175a6.png
  93. BIN
      public/storage/images/20250903/6314d2671a381bd699a09c5218132dba.png
  94. BIN
      public/storage/images/20250903/7b6893e23192ce1c698f3efc113889fa.png
  95. BIN
      public/storage/images/20250903/9387776cdec87b95e27ba20c193f63f0.png
  96. BIN
      public/storage/images/20250903/9a39132e199d12436ced9304071ef0d8.png
  97. BIN
      public/storage/images/20250903/9a8580d6b4af52047cc95d161d803363.png
  98. BIN
      public/storage/images/20250903/af35c91d67e9d55fc0408c6f29e3092c.png
  99. BIN
      public/storage/images/20250903/d2079b48ac384fabdb6d20cfb3ade3e9.png
  100. BIN
      public/storage/images/20250903/d23b791adcbb45937c0ad7e05418cf8c.png

+ 5 - 0
.gitignore
View File

@@ -0,0 +1,5 @@
+/.idea
+/.vscode
+/vendor
+*.log
+.env

+ 1 - 0
.htaccess
View File

@@ -0,0 +1 @@
+ 

+ 42 - 0
.travis.yml
View File

@@ -0,0 +1,42 @@
+sudo: false
+
+language: php
+
+branches:
+  only:
+    - stable
+
+cache:
+  directories:
+    - $HOME/.composer/cache
+
+before_install:
+  - composer self-update
+
+install:
+  - composer install --no-dev --no-interaction --ignore-platform-reqs
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
+  - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
+  - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
+
+script:
+  - php think unit
+
+deploy:
+  provider: releases
+  api_key:
+    secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
+  file:
+    - ThinkPHP_Core.zip
+    - ThinkPHP_Full.zip
+  skip_cleanup: true
+  on:
+    tags: true

+ 26 - 0
404.html
View File

@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+<title>404</title>
+<style>
+	body{
+		background-color:#444;
+		font-size:14px;
+	}
+	h3{
+		font-size:60px;
+		color:#eee;
+		text-align:center;
+		padding-top:30px;
+		font-weight:normal;
+	}
+</style>
+</head>
+
+<body>
+<h3>404,您请求的文件不存在!</h3>
+</body>
+</html>

+ 32 - 0
LICENSE.txt
View File

@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 56 - 0
README.md
View File

@@ -0,0 +1,56 @@
+# ThinkPHP 6.0
+
+> 运行环境要求 PHP7.2+,兼容 PHP8.1
+
+[官方应用服务市场](https://market.topthink.com) | [`ThinkAPI`——官方统一 API 服务](https://docs.topthink.com/think-api)
+
+ThinkPHPV6.0 版本由[亿速云](https://www.yisu.com/)独家赞助发布。
+
+## 主要新特性
+
+- 采用`PHP7`强类型(严格模式)
+- 支持更多的`PSR`规范
+- 原生多应用支持
+- 更强大和易用的查询
+- 全新的事件系统
+- 模型事件和数据库事件统一纳入事件系统
+- 模板引擎分离出核心
+- 内部功能中间件化
+- SESSION/Cookie 机制改进
+- 对 Swoole 以及协程支持改进
+- 对 IDE 更加友好
+- 统一和精简大量用法
+
+## 安装
+
+```
+composer create-project topthink/think tp 6.0.*
+```
+
+如果需要更新框架使用
+
+```
+composer update topthink/framework
+```
+
+## 文档
+
+[完全开发手册](https://www.kancloud.cn/manual/thinkphp6_0/content)
+
+## 参与开发
+
+请参阅 [ThinkPHP 核心框架包](https://github.com/top-think/framework)。
+
+## 版权信息
+
+ThinkPHP 遵循 Apache2 开源协议发布,并提供免费使用。
+
+本项目包含的第三方源码和二进制文件之版权信息另行标注。
+
+版权所有 Copyright © 2006-2021 by ThinkPHP (http://thinkphp.cn)
+
+All rights reserved。
+
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+更多细节参阅 [LICENSE.txt](LICENSE.txt)

+ 1 - 0
app/.htaccess
View File

@@ -0,0 +1 @@
+deny from all

+ 164 - 0
app/AdminBaseController.php
View File

@@ -0,0 +1,164 @@
+<?php
+declare (strict_types = 1);
+
+namespace app;
+
+use think\App;
+use think\exception\ValidateException;
+use think\Validate;
+use think\facade\Db;
+use think\cache\driver\Redis;
+
+
+/**
+ * 控制器基础类
+ */
+abstract class AdminBaseController
+{
+    /**
+     * Request实例
+     * @var \think\Request
+     */
+    protected $request;
+
+    /**
+     * 应用实例
+     * @var \think\App
+     */
+    protected $app;
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batchValidate = false;
+
+    /**
+     * 控制器中间件
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 无需登录鉴权的方法名
+     * @var array
+     */
+    protected $noNeedLogin = [];
+    /**
+     * redis对象
+     * @var array
+     */
+    protected $redis;
+    
+    private $token;
+    
+    
+    /**
+     * 构造方法
+     * @access public
+     * @param  App  $app  应用对象
+     */
+    public function __construct(App $app)
+    {
+        
+        $this->app     = $app;
+        $this->request = $this->app->request;
+        $this->redis = new Redis();
+        // 控制器初始化
+        $this->initialize();
+    }
+
+    // 初始化
+    protected function initialize()
+    {
+        //noNeedLogin全部转换小写
+        foreach ($this->noNeedLogin as &$action) {
+            $action = strtolower($action);
+        }
+        //验证需要鉴权的方法名
+        $callMethodName = strtolower($this->request->action());
+        if (!in_array($callMethodName, $this->noNeedLogin)) {
+            $userToken = Db::table('tb_admin_token')
+                ->where(['token' => $this->request->header('token')])
+                ->where('expired_at', '>', time())
+                ->find();
+            if (!$userToken) {
+                $this->fail(401, '请先登录后再操作');
+            }
+            $this->token = $this->request->header('token');
+        }
+        
+    }
+
+
+    protected function getUser()
+    {
+        $data = Db::table('tb_admin_token')
+            ->where(['token' => $this->token])
+            ->find();
+        return (object) $data;
+    }
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param  array        $data     数据
+     * @param  string|array $validate 验证器名或者验证规则数组
+     * @param  array        $message  提示信息
+     * @param  bool         $batch    是否批量验证
+     * @return array|string|true
+     * @throws ValidateException
+     */
+    protected function validate(array $data, $validate, array $message = [], bool $batch = false)
+    {
+        if (is_array($validate)) {
+            $v = new Validate();
+            $v->rule($validate);
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                [$validate, $scene] = explode('.', $validate);
+            }
+            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+            $v     = new $class();
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        $v->message($message);
+
+        // 是否批量验证
+        if ($batch || $this->batchValidate) {
+            $v->batch(true);
+        }
+
+        return $v->failException(true)->check($data);
+    }
+    
+    protected function success($msg, $data)
+    {
+        echo json_encode([
+            'code' => 200, 
+            'msg'  => $msg,
+            'data' => $data,
+        ]);
+        exit();
+    }
+    
+    protected function fail($code, $msg)
+    {
+        echo json_encode([
+            'code' => $code, 
+            'msg'  => $msg,
+        ]);
+        exit();
+    }
+    
+    //发送TCP消息
+    protected function sendTcpMessage()
+    {
+        
+    }
+
+}

+ 22 - 0
app/AppService.php
View File

@@ -0,0 +1,22 @@
+<?php
+declare (strict_types = 1);
+
+namespace app;
+
+use think\Service;
+
+/**
+ * 应用服务类
+ */
+class AppService extends Service
+{
+    public function register()
+    {
+        // 服务注册
+    }
+
+    public function boot()
+    {
+        // 服务启动
+    }
+}

+ 228 - 0
app/BaseController.php
View File

@@ -0,0 +1,228 @@
+<?php
+declare(strict_types=1);
+
+namespace app;
+
+use think\App;
+use think\exception\ValidateException;
+use think\Validate;
+use think\facade\Db;
+use think\cache\driver\Redis;
+
+
+/**
+ * 控制器基础类
+ */
+abstract class BaseController
+{
+    /**
+     * Request实例
+     * @var \think\Request
+     */
+    protected $request;
+
+    /**
+     * 应用实例
+     * @var \think\App
+     */
+    protected $app;
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batchValidate = false;
+
+    /**
+     * 控制器中间件
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 无需登录鉴权的方法名
+     * @var array
+     */
+    protected $noNeedLogin = [];
+    /**
+     * redis对象
+     * @var array
+     */
+    protected $redis;
+
+    private $token;
+    private $userData = null;
+
+
+    /**
+     * 构造方法
+     * @access public
+     * @param  App  $app  应用对象
+     */
+    public function __construct(App $app)
+    {
+
+        $this->app = $app;
+        $this->request = $this->app->request;
+        $this->redis = new Redis();
+        // 控制器初始化
+        $this->initialize();
+    }
+
+    // 初始化
+    protected function initialize()
+    {
+        //noNeedLogin全部转换小写
+        foreach ($this->noNeedLogin as &$action) {
+            $action = strtolower($action);
+        }
+        //验证需要鉴权的方法名
+        $callMethodName = strtolower($this->request->action());
+        if (!in_array($callMethodName, $this->noNeedLogin)) {
+            $userToken = Db::table('tb_user_token')
+                ->where(['token' => $this->request->header('token')])
+                ->where('expired_at', '>', time())
+                ->find();
+            if (!$userToken) {
+                $this->fail(401, '请先登录后再操作');
+                header('Location: /login');
+            }
+            $this->token = $this->request->header('token');
+        }
+        session_start();
+    }
+
+
+    protected function getUser()
+    {
+        if ($this->userData == null) {
+            $this->userData = Db::table('tb_user_token')
+                ->where(['token' => $this->token])
+                ->find();
+        }
+        return (object) $this->userData;
+    }
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param  array        $data     数据
+     * @param  string|array $validate 验证器名或者验证规则数组
+     * @param  array        $message  提示信息
+     * @param  bool         $batch    是否批量验证
+     * @return array|string|true
+     * @throws ValidateException
+     */
+    protected function validate(array $data, $validate, array $message = [], bool $batch = false)
+    {
+        if (is_array($validate)) {
+            $v = new Validate();
+            $v->rule($validate);
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                [$validate, $scene] = explode('.', $validate);
+            }
+            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+            $v = new $class();
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        $v->message($message);
+
+        // 是否批量验证
+        if ($batch || $this->batchValidate) {
+            $v->batch(true);
+        }
+
+        return $v->failException(true)->check($data);
+    }
+
+    protected function success($msg, $data)
+    {
+        header('Content-Type:application/json; charset=utf-8');
+        echo json_encode([
+            'code' => 200,
+            'msg' => $msg,
+            'data' => $data,
+            'time' => time()
+        ]);
+        exit();
+    }
+
+    protected function fail($code, $msg)
+    {
+        header('Content-Type:application/json; charset=utf-8');
+        echo json_encode([
+            'code' => $code,
+            'msg' => $msg,
+            'time' => time()
+        ]);
+        exit();
+    }
+
+    //发送TCP消息
+    protected function sendTcpMessage()
+    {
+
+    }
+
+    //金币增加
+    protected function addGold($value)
+    {
+        $row = Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->find();
+        Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->update([
+                'gold' => $row['gold'] + $value,
+            ]);
+    }
+
+    //增加经验
+    protected function addExp($value)
+    {
+        $row = Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->find();
+        Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->update([
+                'exp' => $row['exp'] + $value,
+            ]);
+    }
+
+    //增加积分
+    protected function addScore($value)
+    {
+        $row = Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->find();
+        Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->update([
+                'score' => $row['score'] + $value,
+            ]);
+    }
+
+    // 解析获取头像Url
+    public function getUserAvatar($avatar)
+    {
+        if (strpos($avatar, 'system://') === 0) {
+            $data = explode('//', $avatar);
+            $avatarId = isset($data[1]) ? intval($data[1]) : 0;
+            if ($avatarId <= 0) {
+                return $avatar;
+            }
+            $avatarRow = Db::table('tb_system_avatar')
+                ->where(['id' => $avatarId])
+                ->find();
+            return $avatarRow ? $avatarRow['image_url'] : $avatar;
+        }
+        return $avatar;
+    }
+
+}

+ 58 - 0
app/ExceptionHandle.php
View File

@@ -0,0 +1,58 @@
+<?php
+namespace app;
+
+use think\db\exception\DataNotFoundException;
+use think\db\exception\ModelNotFoundException;
+use think\exception\Handle;
+use think\exception\HttpException;
+use think\exception\HttpResponseException;
+use think\exception\ValidateException;
+use think\Response;
+use Throwable;
+
+/**
+ * 应用异常处理类
+ */
+class ExceptionHandle extends Handle
+{
+    /**
+     * 不需要记录信息(日志)的异常类列表
+     * @var array
+     */
+    protected $ignoreReport = [
+        HttpException::class,
+        HttpResponseException::class,
+        ModelNotFoundException::class,
+        DataNotFoundException::class,
+        ValidateException::class,
+    ];
+
+    /**
+     * 记录异常信息(包括日志或者其它方式记录)
+     *
+     * @access public
+     * @param  Throwable $exception
+     * @return void
+     */
+    public function report(Throwable $exception): void
+    {
+        // 使用内置的方式记录异常日志
+        parent::report($exception);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @access public
+     * @param \think\Request   $request
+     * @param Throwable $e
+     * @return Response
+     */
+    public function render($request, Throwable $e): Response
+    {
+        // 添加自定义异常处理机制
+
+        // 其他错误交给系统处理
+        return parent::render($request, $e);
+    }
+}

+ 8 - 0
app/Request.php
View File

@@ -0,0 +1,8 @@
+<?php
+namespace app;
+
+// 应用请求对象类
+class Request extends \think\Request
+{
+
+}

+ 2 - 0
app/common.php
View File

@@ -0,0 +1,2 @@
+<?php
+// 应用公共文件

+ 1691 - 0
app/controller/Admin.php
View File

@@ -0,0 +1,1691 @@
+<?php
+namespace app\controller;
+
+
+use app\AdminBaseController;
+use think\facade\Db;
+use phpu\facade\ThinkCaptcha;
+use Ramsey\Uuid\Uuid;
+
+
+//后台控制器
+class Admin extends AdminBaseController
+{
+    protected $noNeedLogin = ['login', 'logout', 'getAdminCaptchaImage'];
+    
+    //获取管理员登录验证码图片
+    public function getAdminCaptchaImage()
+    {
+        return ThinkCaptcha::printImg('admin');
+    }
+    
+    //登录
+    public function login()
+    {
+        $username = $this->request->post('username');
+        $password = $this->request->post('password');
+        //$captcha = $this->request->post('captcha');
+        if (empty($username) || empty($password)) {
+            $this->fail(500, '参数校验错误');
+        }
+        //if (!ThinkCaptcha::check($captcha, 'admin', 2)) {
+        //    $this->fail(501, '验证码错误');
+        //}
+        $admin = Db::table('tb_admin')
+            ->where(['username' => $username])
+            ->where(['password' => md5($password)])
+            ->find();
+        if (!$admin) {
+            $this->fail(502, '管理员用户名或密码错误');
+        }
+        //写登录日志
+        Db::table('tb_admin_login_log')
+            ->insert([
+                'user_id'    => $admin['id'],
+                'ip'         => $this->request->ip(),
+                'user_agent' => $this->request->header('user-agent'),
+                'created_at' => time(),
+            ]);
+        //写登录Token
+        $token = Uuid::uuid4()->toString();
+        Db::table('tb_admin_token')
+            ->insert([
+                'user_id'    => $admin['id'],
+                'token'      => $token,
+                'expired_at' => time() + 24 * 60 * 60,
+            ]);
+        $this->success('success', [
+            'token' => $token,
+        ]);
+    }
+    
+    //退出登录
+    public function logout()
+    {
+        $token = $this->request->post('token');
+        if (empty($token)) {
+            $this->fail(500, 'token参数校验错误');
+        }
+        Db::table('tb_admin_token')
+            ->where(['token' => $token])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //修改密码,修改后前端需要跳到登录页重新登录
+    public function changePwd()
+    {
+        $oldPassword = $this->request->post('oldPassword');
+        $newPassword = $this->request->post('newPassword');
+        if (empty($oldPassword) || empty($newPassword)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = $this->getUser();
+        $data = Db::table('tb_user')
+            ->where(['id' => $user->user_id])
+            ->find();
+        if ($data['password'] != md5($oldPassword)) {
+            $this->fail(501, '当前密码错误');
+        }
+        Db::table('tb_user')
+            ->where(['id' => $user->user_id])
+            ->update([
+                'password' => md5($newPassword),
+            ]);
+        //释放token
+        Db::table('tb_user_token')
+            ->where(['token' => $user->token])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //获取控制台数据
+    public function getConsoleData()
+    {
+        $userCount = Db::table('tb_user')->count();
+        $todayUserCount = Db::table('tb_user')
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->count();
+        $totalWebsitePay = Db::table('tb_user_order')
+            ->where(['state' => 1])
+            ->sum('balance');
+        $todayWebsitePay = Db::table('tb_user_order')
+            ->where(['state' => 1])
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->sum('balance');
+        $totalTaobaoPay = Db::table('tb_user_game')
+            ->where(['channel' => 2])
+            ->sum('price');
+        $todayTaobaoPay = Db::table('tb_user_game')
+            ->where(['channel' => 2])
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->sum('price');
+        $totalPddPay = Db::table('tb_user_game')
+            ->where(['channel' => 3])
+            ->sum('price');
+        $todayPddPay = Db::table('tb_user_game')
+            ->where(['channel' => 3])
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->sum('price');
+        $this->success('success', [
+            //用户数据
+            'userData' => [
+                'totalUserCount' => $userCount,       //总会员数
+                'todayUserCount' => $todayUserCount,  //今日新增会员数
+            ],
+            //网站消费数据
+            'websitePayData' => [
+                'totalPay' => number_format(floatval($totalWebsitePay), 2),       //网站总直充
+                'todayPay' => number_format(floatval($todayWebsitePay), 2),       //今日网站直充
+            ],
+            //淘宝平台消费数据
+            'taobaoPayData' => [
+                'totalPay' => number_format(floatval($totalTaobaoPay), 2),        //淘宝平台总消费
+                'todayPay' => number_format(floatval($todayTaobaoPay), 2),        //淘宝平台今日消费
+            ],
+            //拼多多平台消费数据
+            'pddPayData' => [
+                'totalPay' => number_format(floatval($totalPddPay), 2),           //拼多多平台总消费
+                'todayPay' => number_format(floatval($todayPddPay), 2),           //拼多多平台今日消费
+            ],
+        ]);
+    }
+    
+    //获取用户数据 
+    public function getUserList()
+    {
+        // 获取分页参数,确保有默认值
+        $pageSize = $this->request->post('pageSize', 10);  // 每页显示的数据数量
+        $pageNum = $this->request->post('pageNum', 1);    // 当前页码
+        
+        // 参数校验
+        if (!is_numeric($pageSize) || !is_numeric($pageNum)) {
+            $this->fail(500, '分页参数校验错误');
+        }
+        
+        // 转换为整数
+        $pageSize = intval($pageSize);
+        $pageNum = intval($pageNum);
+        
+        // 确保最小值
+        $pageSize = max(1, $pageSize);
+        $pageNum = max(1, $pageNum);
+        
+        // 获取搜索参数
+        $keyword = $this->request->post('keyword', '');
+        $startDate = $this->request->post('startDate', '');
+        $endDate = $this->request->post('endDate', '');
+        
+        // 构建查询
+        $query = Db::table('tb_user')
+            ->field('id,username,created_at,last_login,login_count,last_login_ip,avatar,balance,email,state');
+        
+        // 添加搜索条件
+        if (!empty($keyword)) {
+            $query->where(function($q) use ($keyword) {
+                $q->where('username', 'like', "%{$keyword}%")
+                  ->whereOr('id', '=', $keyword)
+                  ->whereOr('email', 'like', "%{$keyword}%");
+            });
+        }
+        
+        // 添加日期范围条件
+        if (!empty($startDate)) {
+            $query->where('created_at', '>=', $startDate . ' 00:00:00');
+        }
+        if (!empty($endDate)) {
+            $query->where('created_at', '<=', $endDate . ' 23:59:59');
+        }
+        
+        // 排序
+        $query->order('created_at', 'DESC');
+        
+        // 分页查询
+        $paginator = $query->paginate([
+            'list_rows' => $pageSize,
+            'page' => $pageNum,
+            'path' => '' // 保持URL简洁
+        ]);
+        
+        // 处理头像数据
+        $users = $paginator->getCollection()->each(function($user) {
+            if (str_starts_with($user['avatar'], 'system://')) {
+                $data = explode('//', $user['avatar']);
+                $avatarId = intval($data[1]);
+                $avatar = Db::table('tb_system_avatar')
+                    ->where(['id' => $avatarId])
+                    ->find();
+                $user['avatar'] = $avatar['image_url'] ?? '';
+            }
+            return $user;
+        });
+        
+        // 返回分页数据
+        $this->success('success', [
+            'data' => $users,
+            'total' => $paginator->total(),
+            'current_page' => $paginator->currentPage(),
+            'per_page' => $paginator->listRows(),
+            'last_page' => $paginator->lastPage(),
+        ]);
+    }
+    
+    //用户封号或解封
+    public function banUser()
+    {
+        $userId = $this->request->post('userId');   //用户ID
+        $state = $this->request->post('state');     //状态  1:设置为封号  0:设置为解封
+        if (!is_numeric($userId) || !is_numeric($state)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->find();
+        if (!$user) {
+            $this->fail(501, 'userId不存在');
+        }
+        if (intval($state) == 0) {
+            $state = 1;
+        } else {
+            $state = 2;
+        }
+        Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->update(['state' => $state]);
+        $this->success('success', null);
+    }
+ 
+    //新增游戏分类
+    public function addGameCategory()
+    {
+        $name = $this->request->post('name');
+        $imageUrl = $this->request->post('imageUrl');
+        $description = $this->request->post('description');
+        $priority = $this->request->post('priority');
+        if (empty($name) || empty($imageUrl) || empty($description) || !is_numeric($priority)) {
+            $this->fail(500, '参数校验错误');
+        }
+        Db::table('tb_game_category')
+            ->insert([
+                'name'        => $name,
+                'image_url'   => $imageUrl,
+                'description' => $description,
+                'priority'    => $priority,
+                'created_at'  => time(),
+                'updated_at'  => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //获取游戏分类,无需分类
+    public function getGameCategory()
+    {
+        $data = Db::table('tb_game_category')
+            ->order('priority DESC')
+            ->select();
+        $this->success('success', $data);
+    }
+ 
+    //编辑游戏分类
+    public function editGameCategory()
+    {
+        $categoryId = $this->request->post('categoryId');
+        $name = $this->request->post('name');
+        $imageUrl = $this->request->post('imageUrl');
+        $description = $this->request->post('description');
+        $priority = $this->request->post('priority');
+        if (empty($name) || empty($imageUrl) || empty($description) || !is_numeric($priority) || !is_numeric($categoryId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        Db::table('tb_game_category')
+            ->where(['id' => $categoryId])
+            ->update([
+                'name'        => $name,
+                'image_url'   => $imageUrl,
+                'description' => $description,
+                'priority'    => $priority,
+                'updated_at'  => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //删除游戏分类
+    public function removeGameCategory()
+    {
+        $categoryId = $this->request->post('categoryId');
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $data = Db::table('tb_game_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$data) {
+            $this->fail(501, 'categoryId不存在');
+        }
+        Db::table('tb_game_category')
+            ->where(['id' => $categoryId])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    public function getGameList()
+    {
+        $categoryId = $this->request->post('categoryId');  // 游戏分类ID
+        $pageNum = $this->request->post('pageNum');        // 当前页码
+        $pageSize = $this->request->post('pageSize');      // 每页数量
+        
+        if (!is_numeric($categoryId) || !is_numeric($pageNum) || !is_numeric($pageSize)) {
+            $this->fail(500, '参数校验错误');
+        }
+        
+        $query = Db::table('tb_game')->where(['category' => $categoryId]);
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->page(intval($pageNum), intval($pageSize))->select();
+        
+        $this->success('success', [
+            'list' => $list,
+            'total' => $total,
+            'pageNum' => intval($pageNum),
+            'pageSize' => intval($pageSize)
+        ]);
+    }
+    
+    //新增游戏
+    public function addGame()
+    {
+        $categoryId = $this->request->post('categoryId');   //游戏分类ID
+        $title = $this->request->post('title');             //标题
+        $imageUrl = $this->request->post('imageUrl');       //图片URL
+        $price = $this->request->post('price');             //价格
+        $description = $this->request->post('description'); //描述
+        $priority = $this->request->post('priority');       //优先级,越小越靠前
+        $browse = $this->request->post('browse');           //浏览次数,营销需要,添加的时候可以自定义
+        $buyCount = $this->request->post('buyCount');       //购买次数,营销需要,添加的时候可以自定义
+        $downloadUrl = $this->request->post('downloadUrl'); //下载链接
+        $extractedCode = $this->request->post('extractedCode'); //提取码
+        $gameVersion = $this->request->post('gameVersion');   //游戏版本
+        $language = $this->request->post('language');         //游戏语言
+
+
+        
+        if (!is_numeric($categoryId) || empty($title) || empty($imageUrl) || !is_float($price) || empty($description) || !is_numeric($priority) || !is_numeric($browse) || !is_numeric($buyCount) || empty($downloadUrl) || empty($extractedCode) || empty($gameVersion) || empty($language)) {
+            $this->fail(500, '参数校验错误');
+        }
+        //判断游戏分类ID是否存在
+        $category = Db::table('tb_game_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$category) {
+            $this->fail(501, '游戏分类ID不存在');
+        }
+        Db::table('tb_game')
+            ->insert([
+                'category'       => $categoryId,
+                'title'          => $title,
+                'image_url'      => $imageUrl,
+                'price'          => $price,
+                'description'    => $description,
+                'priority'       => $priority,
+                'browse'         => $browse,
+                'buy_count'      => $buyCount,
+                'game_version'   => $gameVersion,
+                'language'       => $language,
+                'created_at'     => time(),
+                'updated_at'     => time(),
+                'download_url'   => $downloadUrl,
+                'extracted_code' => $extractedCode,
+            ]);
+        $this->success('success', null);
+    }
+    
+    //编辑游戏信息
+    public function editGame()
+    {
+        $gameId = $this->request->post('gameId');           //游戏ID  
+        $categoryId = $this->request->post('categoryId');   //游戏分类ID
+        $title = $this->request->post('title');             //标题
+        $imageUrl = $this->request->post('imageUrl');       //图片URL
+        $price = $this->request->post('price');             //价格
+        $description = $this->request->post('description'); //描述
+        $priority = $this->request->post('priority');       //优先级,越小越靠前
+        $browse = $this->request->post('browse');           //浏览次数, 营销需要,修改的时候可以自定义
+        $buyCount = $this->request->post('buyCount');       //购买次数,营销需要,修改的时候可以自定义
+        $downloadUrl = $this->request->post('downloadUrl'); //下载链接
+        $extractedCode = $this->request->post('extractedCode'); //提取码
+        $language = $this->request->post('language');       //游戏语言
+        $gameVersion = $this->request->post('gameVersion'); //游戏版本
+        
+        
+        if (!is_numeric($categoryId) || empty($title) || empty($imageUrl) || !is_float($price) || empty($description) || !is_numeric($priority) || !is_numeric($browse) || !is_numeric($buyCount) || !is_numeric($gameId) || empty($downloadUrl) || empty($extractedCode) || empty($language) || empty($gameVersion)) {
+            $this->fail(500, '参数校验错误');
+        }
+        //判断游戏分类ID是否存在
+        $category = Db::table('tb_game_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$category) {
+            $this->fail(501, '游戏分类ID不存在');
+        }
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(502, '游戏ID不存在');
+        }
+        Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->update([
+                'category'       => $categoryId,
+                'title'          => $title,
+                'image_url'      => $imageUrl,
+                'price'          => $price,
+                'description'    => $description,
+                'priority'       => $priority,
+                'browse'         => $browse,
+                'buy_count'      => $buyCount,
+                'updated_at'     => time(),
+                'download_url'   => $downloadUrl,
+                'extracted_code' => $extractedCode,
+                'language'       => $language,
+                'game_version'   => $gameVersion
+            ]);
+        $this->success('success', null);
+    }
+    
+    //删除游戏 
+    public function removeGame()
+    {
+        $gameId = $this->request->post('gameId');           //游戏ID  
+        if (!is_numeric($gameId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(502, '游戏ID不存在');
+        }
+        Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //获取单个游戏信息
+    public function getGame()
+    {
+        $gameId = $this->request->post('gameId');  //游戏ID 
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(502, '游戏ID不存在');
+        }
+        $this->success('success', $game);
+    }
+    
+    //获取所有的CDKEY
+    public function getCdKeyList()
+    {
+        // 获取分页参数
+        $pageNum = $this->request->post('pageNum', 10);
+        if (!is_numeric($pageNum)) {
+            $this->fail(500, 'pageNum参数校验错误');
+        }
+        
+        // 获取搜索参数
+        $user_id = $this->request->post('user_id', '');
+        $createTimeRange = $this->request->post('createTimeRange', []);
+        
+        // 构建查询条件
+        $query = Db::table('tb_cdkey');
+        
+        // 用户ID搜索
+        if (!empty($user_id)) {
+            $query->where('user_id', $user_id);
+        }
+        
+        // 创建时间范围搜索(时间戳直接比较)
+        if (!empty($createTimeRange) && count($createTimeRange) == 2) {
+            $startTime = $createTimeRange[0];
+            $endTime = $createTimeRange[1];
+            $query->whereBetween('created_at', [$startTime, $endTime]);
+        }
+        
+        // 执行分页查询
+        $data = $query->order('created_at DESC')
+                     ->paginate(intval($pageNum));
+        
+        $this->success('success', $data);
+    }
+    //批量创建CDKEY
+    public function addCdKeyBatch()
+    {
+        $num = $this->request->post('num');          //生成CDKEY的数量
+        $days = $this->request->post('days');        //有效期天数
+        $gameId = $this->request->post('gameId');    //CDKEY关联的购买游戏ID
+        $channel = $this->request->post('channel');  //CDKEY销售渠道:1:淘宝 2:拼多多
+        if (!is_numeric($num) || !is_numeric($days) || !is_numeric($gameId) || !is_numeric($channel)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(501, '游戏ID不存在');
+        }
+        $batchData = [];
+        for ($i = 0; $i < $num; $i++) {
+            $prefix = '';
+            if ($channel == 1) {
+                $prefix = 'tb';
+            } elseif ($channel == 2) {
+                $prefix = 'pdd';
+            }
+            $cdKey = $prefix . '-' . Uuid::uuid4()->toString();
+            $data = [
+                'cdkey'      => $cdKey,
+                'game_id'    => $gameId,
+                'expired_at' => time() + 60 * 60 * 24 * intval($days),
+                'state'      => 0, //未使用状态
+                'created_at' => time(),
+            ];
+            Db::table('tb_cdkey')
+                ->insert($data);
+            $batchData[] = $data;
+        }
+        $this->success('success', $batchData);
+    }
+    
+    //删除CDKEY
+    public function removeCdKey()
+    {
+        $cdKey = $this->request->post('cdKey');   //CDKEY
+        if (empty($cdKey)) {
+            $this->fail(500, 'cdKey参数校验错误');
+        }
+        $data = Db::table('tb_cdkey')
+            ->where(['cdkey' => $cdKey])
+            ->find();
+        if (!$data) {
+            $this->fail(501, 'cdKey不存在');
+        }
+        Db::table('tb_cdkey')
+            ->where(['cdkey' => $cdKey])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //获取充值订单列表
+public function getOrderList()
+{
+    $pageSize = $this->request->post('pageSize', 10);  // 每页数量
+    $pageNum = $this->request->post('pageNum', 1);    // 当前页码
+    
+    // 参数校验
+    if (!is_numeric($pageSize) || !is_numeric($pageNum)) {
+        $this->fail(500, '参数校验错误');
+    }
+    
+    // 搜索条件
+    $startDate = $this->request->post('startDate', '');
+    $endDate = $this->request->post('endDate', '');
+    
+    $query = Db::table('tb_user_order')->order('created_at DESC');
+    
+    // 添加日期范围 - 修复时间查询
+    if (!empty($startDate)) {
+        // 将日期转换为时间戳格式进行比较
+        $startTimestamp = strtotime($startDate);
+        $query->where('created_at', '>=', $startTimestamp);
+    }
+    if (!empty($endDate)) {
+        // 结束日期需要包含一整天,所以加上86399秒(23:59:59)
+        $endTimestamp = strtotime($endDate) + 86399;
+        $query->where('created_at', '<=', $endTimestamp);
+    }
+    
+    $data = $query->paginate([
+        'list_rows' => $pageSize,
+        'page' => $pageNum
+    ]);
+    
+    $this->success('success', $data);
+}
+    
+    //上传图片
+    public function uploadImage()
+    {
+        $file = $this->request->file('file');
+        if (empty($file)) {
+            $this->fail(500, '上传的图片文件不能为空');
+        }
+        // 使用验证器验证上传的文件
+        validate(['file' => [
+            // 限制文件大小(单位b),这里限制为2M
+            'fileSize' => 200 * 1024 * 1024,
+            // 限制文件后缀,多个后缀以英文逗号分割
+            'fileExt'  => 'jpg,jpeg,png',
+        ]])->check(['file' => $file]);
+        $saveName = \think\facade\Filesystem::disk('public')->putFile('upload', $file);
+        $this->success('success', [
+            'filePath' => $this->request->scheme() . '://' . $this->request->host() . '/storage/' . $saveName,
+        ]);
+    }
+    
+public function uploadFile()
+{
+    $file = $this->request->file('file');
+    if (empty($file)) {
+        $this->fail(500, '上传的文件不能为空');
+    }
+
+    // 使用验证器验证上传的文件
+    validate(['file' => [
+        // 限制文件大小(单位b),这里限制为50M
+        'fileSize' => 50 * 1024 * 1024,
+        // 限制文件后缀,多个后缀以英文逗号分割,支持压缩包、文档和图片
+        'fileExt'  => 'zip,rar,7z,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,jpg,jpeg,png,gif,bmp',
+    ]])->check(['file' => $file]);
+
+    $saveName = \think\facade\Filesystem::disk('public')->putFile('upload', $file);
+    $filePath = $this->request->scheme() . '://' . $this->request->host() . '/storage/' . $saveName;
+    $fileSize = $file->getSize(); // 获取文件大小
+
+    $this->success('success', [
+        'filePath' => $filePath,
+        'fileSize' => $fileSize,
+    ]);
+}
+    
+    //后台手动给用户上下余额
+    //amount为正整数给用户加余额,如果为负数则给用户减余额
+    public function addUserBalance()
+    {
+        $userId = $this->request->post('userId');
+        $amount = $this->request->post('amount');
+        if (!is_numeric($amount) || !is_numeric($userId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->find();
+        if (!$user) {
+            $this->fail(501, '找不到userId的用户');
+        }
+        Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->update([
+                'balance' => $amount > 0 ? $user['balance'] + $amount : $user['balance'] + $amount,
+            ]);
+        //写记录
+        Db::table('tb_user_balance_record')
+            ->insert([
+                'user_id'    => $userId,
+                'amount'     => $amount,
+                'created_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //获取用户购买游戏的记录
+    public function getUserGames()
+    {
+        $userId = $this->request->post('userId');
+        if (!is_numeric($userId)) {
+            $this->fail(500, 'userId参数校验错误');
+        }
+        $user = Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->find();
+        if (!$user) {
+            $this->fail(501, 'userId不存在');
+        }
+        $data = Db::table('tb_user_game')
+            ->where(['user_id' => $userId])
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    
+    //获取管理员手动变动用户余额记录
+    public function getUserBalanceRecord()
+    {
+        $userId = $this->request->post('userId');
+        $pageNum = $this->request->post('pageNum', 1);
+        $pageSize = $this->request->post('pageSize', 10);
+        
+        if (!is_numeric($userId)) {
+            $this->fail(500, 'userId参数校验错误');
+        }
+        
+        $user = Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->find();
+        if (!$user) {
+            $this->fail(501, 'userId不存在');
+        }
+        
+        $query = Db::table('tb_user_balance_record')
+            ->where(['user_id' => $userId]);
+        
+        $total = $query->count();
+        $data = $query->order('created_at', 'desc')
+            ->limit(($pageNum - 1) * $pageSize, $pageSize)
+            ->select();
+        
+        $this->success('success', [
+            'data' => $data,
+            'total' => $total
+        ]);
+    }
+    
+    //获取VIP用户列表
+    public function getVipUserList()
+    {
+        
+        $page = input('page', 1);
+        $pageSize = input('pageSize', 10);
+        
+        
+        $userId = input('user_id', '');
+        $type = input('type', '');
+        $createdAtStart = input('created_at_start', '');
+        $createdAtEnd = input('created_at_end', '');
+        $expiredAtStart = input('expired_at_start', '');
+        $expiredAtEnd = input('expired_at_end', '');
+        $search = input('search', '');
+        
+        $query = Db::table('tb_user_vip');
+        
+        
+        if (!empty($userId)) {
+            $query->where('user_id', $userId);
+        }
+        
+        
+        if (!empty($type)) {
+            $query->where('type', $type);
+        }
+        
+        
+        if (!empty($createdAtStart) && !empty($createdAtEnd)) {
+            $query->where('created_at', '>=', $createdAtStart)
+                  ->where('created_at', '<=', $createdAtEnd);
+        }
+        
+        
+        if (!empty($expiredAtStart) && !empty($expiredAtEnd)) {
+            $query->where('expired_at', '>=', $expiredAtStart)
+                  ->where('expired_at', '<=', $expiredAtEnd);
+        }
+        
+        
+        if (!empty($search)) {
+            $query->where(function($q) use ($search) {
+                $q->where('user_id', 'like', "%{$search}%");
+                
+            });
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $data = $query->order('created_at DESC')
+                     ->limit(($page - 1) * $pageSize, $pageSize)
+                     ->select();
+        
+        // 返回结果
+        return $this->success('success', [
+            'data' => $data,
+            'total' => $total,
+            'page' => $page,
+            'pageSize' => $pageSize
+        ]);
+    }
+    
+    
+    
+    public function getCustomerList()
+    {
+        
+        $data = Db::table('tb_kefu')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    public function addCustomerList()
+    {
+        $name = $this->request->post('name');  
+        $jumpUrl = $this->request->post('jumpUrl'); 
+        $type = $this->request->post('type'); 
+
+        if (!is_numeric($type) || empty($name) || empty($jumpUrl)) {
+            $this->fail(500, '参数校验错误');
+        }
+        
+        Db::table('tb_kefu')
+            ->insert([
+                'name'           => $name,
+                'jump_url'       => $jumpUrl,
+                'type'           => $type,
+                'created_at'     => time(),
+                'updated_at'     => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    public function editCustomerList()
+    {
+        $name = $this->request->post('name');  
+        $jumpUrl = $this->request->post('jumpUrl'); 
+        $type = $this->request->post('type'); 
+        $id = $this->request->post('id'); 
+        
+        if (!is_numeric($type) || empty($name) || empty($jumpUrl)) {
+            $this->fail(500, '参数校验错误');
+        }
+        
+        $game = Db::table('tb_kefu')
+            ->where(['id' => $id])
+            ->find();
+        if (!$game) {
+            $this->fail(502, '数据不存在');
+        }
+        Db::table('tb_kefu')
+            ->where(['id' => $id])
+            ->update([
+                'name'       => $name,
+                'jump_url'      => $jumpUrl,
+                'type'          => $type,
+                'created_at'     => time(),
+                'updated_at'     => time(),
+                
+            ]);
+        $this->success('success', null);
+    }
+    
+    
+    public function deleteCustomerList()
+    {
+        $id = $this->request->post('id');   //CDKEY
+        if (empty($id)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $data = Db::table('tb_kefu')
+            ->where(['id' => $id])
+            ->find();
+        if (!$data) {
+            $this->fail(501, '数据不存在');
+        }
+        Db::table('tb_kefu')
+            ->where(['id' => $id])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    
+    
+    //获取用户访问网站记录,用于统计用户行为
+    public function getVisitRecord()
+    {
+        // 获取分页参数,确保有默认值
+        $pageSize = $this->request->post('pageSize', 10);  // 每页显示的数据数量
+        $pageNum = $this->request->post('pageNum', 1);    // 当前页码
+        
+        // 获取日期筛选参数
+        $startDate = $this->request->post('startDate', '');
+        $endDate = $this->request->post('endDate', '');
+        
+        // 构建查询
+        $query = Db::table('tb_website_visit');
+        
+        // 添加日期筛选条件
+        if (!empty($startDate)) {
+            $startTimestamp = strtotime($startDate . ' 00:00:00');
+            $query->where('created_at', '>=', $startTimestamp);
+        }
+        
+        if (!empty($endDate)) {
+            $endTimestamp = strtotime($endDate . ' 23:59:59');
+            $query->where('created_at', '<=', $endTimestamp);
+        }
+        
+        // 获取总数和数据
+        $total = $query->count();
+        $data = $query
+            ->order('created_at DESC')
+            ->page(intval($pageNum), intval($pageSize))
+            ->select();
+            
+        $this->success('success', [
+            'data' => $data,   // 当前页数据
+            'total' => $total, // 总记录数
+        ]);
+    }
+    
+    
+    //获取日均,总PV,日均,总IP访问数据
+    public function visitData()
+    {
+        $totalPV = Db::table('tb_website_visit')->count();
+        $todayPV = Db::table('tb_website_visit')
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->count();
+        $totalIP = Db::table('tb_website_visit')
+            ->distinct(true)
+            ->count('ip_address');
+        $todayIP = Db::table('tb_website_visit')
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->distinct(true)
+            ->count('ip_address');
+        $this->success('success', [
+            'totalPV' => $totalPV,   //总PV
+            'todayPV' => $todayPV,   //今日PV
+            'todayIP' => $todayIP,   //今日IP
+            'totalIP' => $totalIP,   //总IP
+        ]);
+    }
+    
+    
+    //获取用户组
+    public function getUserGroup()
+    {
+        $name = $this->request->post('name', '');
+        $level = $this->request->post('level', '');
+        $startDate = $this->request->post('start_date', '');
+        $endDate = $this->request->post('end_date', '');
+        
+        $query = Db::table('tb_user_group');
+        
+        // 按名称搜索
+        if (!empty($name)) {
+            $query->where('name', 'like', '%' . $name . '%');
+        }
+        
+        // 按等级搜索
+        if (!empty($level) && is_numeric($level)) {
+            $query->where('level', $level);
+        }
+        
+        // 按时间范围搜索
+        if (!empty($startDate)) {
+            $startTimestamp = strtotime($startDate);
+            $query->where('created_at', '>=', $startTimestamp);
+        }
+        
+        if (!empty($endDate)) {
+            $endTimestamp = strtotime($endDate . ' 23:59:59');
+            $query->where('created_at', '<=', $endTimestamp);
+        }
+        
+        $data = $query->order('created_at DESC')->select();
+        $this->success('success', $data);
+    }
+    //添加用户组
+    public function addUserGroup()
+    {
+        $name = $this->request->post('name');     //用户组名称
+        $level = $this->request->post('level');   //等级
+        $exp = $this->request->post('exp');       //所需经验值
+        if (empty($name) || !is_numeric($level) || !is_numeric($exp)) {
+            $this->fail(500, '参数校验错误');
+        }
+        Db::table('tb_user_group')
+            ->insert([
+                'name'       => $name,
+                'level'      => $level,
+                'exp'        => $exp,
+                'created_at' => time(),
+                'updated_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //编辑用户组
+    public function editUserGroup()
+    {
+        $id = $this->request->post('id');         //用户组ID
+        $name = $this->request->post('name');     //用户组名称
+        $level = $this->request->post('level');   //等级
+        $exp = $this->request->post('exp');       //所需经验值
+        if (empty($name) || !is_numeric($id) || !is_numeric($level) || !is_numeric($exp)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $userGroup = Db::table('tb_user_group')
+            ->where(['id' => $id])
+            ->find();
+        if (!$userGroup) {
+            $this->fail(501, '找不到用户组ID');
+        }
+        Db::table('tb_user_group')
+            ->where(['id' => $id])
+            ->update([
+                'name'       => $name,
+                'level'      => $level,
+                'exp'        => $exp,
+                'updated_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //删除用户组
+    public function removeUserGroup()
+    {
+        $id = $this->request->post('id');         //用户组ID
+        if (!is_numeric($id)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $userGroup = Db::table('tb_user_group')
+            ->where(['id' => $id])
+            ->find();
+        if (!$userGroup) {
+            $this->fail(501, '找不到用户组ID');
+        }
+        Db::table('tb_user_group')
+            ->where(['id' => $id])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    
+    // 获取用户消息,支持分页和时间筛选
+    public function getUserMessage()
+    {
+        $page = $this->request->post('page', 1);
+        $limit = $this->request->post('limit', 10);
+        $startDate = $this->request->post('start_date', '');
+        $endDate = $this->request->post('end_date', '');
+        
+        $query = Db::table('tb_message');
+        
+        // 按时间范围搜索
+        if (!empty($startDate)) {
+            $startTimestamp = strtotime($startDate);
+            $query->where('created_at', '>=', $startTimestamp);
+        }
+        
+        if (!empty($endDate)) {
+            $endTimestamp = strtotime($endDate . ' 23:59:59');
+            $query->where('created_at', '<=', $endTimestamp);
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 分页查询
+        $data = $query->order('created_at DESC')
+                     ->limit(($page - 1) * $limit, $limit)
+                     ->select();
+        
+        $this->success('success', [
+            'data' => $data,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit
+        ]);
+    }
+    
+    //获取所有论坛版块列表
+public function getForumCategoryList()
+{
+    $categoryId = $this->request->post('categoryId');
+    $page = $this->request->post('page', 1);
+    $limit = $this->request->post('limit', 10);
+    
+    // 构建查询条件
+    $query = Db::table('tb_forum_category');
+    
+    if (!empty($categoryId)) {
+        $query->where('category_id', $categoryId);
+    }
+    
+    // 获取总数
+    $total = $query->count();
+    
+    // 获取分页数据
+    $data = $query->order('created_at DESC')
+                 ->page($page, $limit)
+                 ->select();
+    
+    $this->success('success', [
+        'list' => $data,
+        'total' => $total,
+        'page' => $page,
+        'limit' => $limit
+    ]);
+}
+    
+    
+    public function getForumThreadCategory()
+    {
+        $categoryid = $this->request->post('forumId');
+        $data = Db::table('tb_forum_thread_category')->where('category_id',$categoryid)
+            ->order('created_at DESC')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+public function saveThreadCategories()
+{
+    // 获取前端传来的板块ID和分类数据
+    $forumId = $this->request->post('forumId');
+    $categories = $this->request->post('categories');
+
+    // 开启数据库事务
+    Db::startTrans();
+    try {
+        // 获取当前板块的所有帖子分类
+        $existingCategories = Db::table('tb_forum_thread_category')
+            ->where('category_id', $forumId)
+            ->select()
+            ->toArray();
+
+        // 将现有分类的 ID 存入数组,方便后续对比
+        $existingCategoryIds = array_column($existingCategories, 'id');
+
+        // 收集新分类的ID(包括已有的和新增的)
+        $newCategoryIds = [];
+        
+        // 遍历前端传来的分类数据
+        foreach ($categories as $category) {
+            if (isset($category['id']) && !empty($category['id'])) { 
+                // 如果有 ID,表示是已存在的分类,需要更新
+                $newCategoryIds[] = $category['id'];
+                
+                // 检查是否需要更新
+                $existingCategory = array_filter($existingCategories, function($item) use ($category) {
+                    return $item['id'] == $category['id'];
+                });
+
+                if (!empty($existingCategory)) {
+                    $existingCategory = array_values($existingCategory);
+                    if ($existingCategory[0]['title'] != $category['title']) {
+                        // 更新分类
+                        Db::table('tb_forum_thread_category')
+                            ->where('id', $category['id'])
+                            ->update(['title' => $category['title'], 'updated_at' => time()]);
+                    }
+                }
+            } else { 
+                // 如果没有 ID,表示是新增的分类
+                // 插入新的分类
+                $newId = Db::table('tb_forum_thread_category')
+                    ->insertGetId([
+                        'category_id' => $forumId,
+                        'title' => $category['title'],
+                        'created_at' => time(),
+                        'updated_at' => time()
+                    ]);
+                
+                // 将新插入的ID加入到数组中
+                $newCategoryIds[] = $newId;
+            }
+        }
+
+        // 删除被移除的分类
+        if (!empty($newCategoryIds)) {
+            $deletedCategoryIds = array_diff($existingCategoryIds, $newCategoryIds);
+            if (!empty($deletedCategoryIds)) {
+                Db::table('tb_forum_thread_category')
+                    ->where('id', 'in', $deletedCategoryIds)
+                    ->delete();
+            }
+        } else {
+            // 如果没有新分类,删除所有现有分类
+            if (!empty($existingCategoryIds)) {
+                Db::table('tb_forum_thread_category')
+                    ->where('id', 'in', $existingCategoryIds)
+                    ->delete();
+            }
+        }
+
+        // 提交事务
+        Db::commit();
+        return json(['code' => 200, 'msg' => '保存成功']);
+    } catch (\Exception $e) {
+        // 回滚事务
+        Db::rollback();
+        return json(['code' => 500, 'msg' => '保存失败:' . $e->getMessage()]);
+    }
+}
+    
+    //添加论坛版块
+    public function addForumCategory()
+    {
+        $name = $this->request->post('name');                 //版块名称
+        $description = $this->request->post('description');   //版块描述
+        $imageUrl = $this->request->post('imageUrl');         //版块图片URL,需要用uploadImage接口先上传拿到URL
+        $adminId = $this->request->post('adminId');           //版主用户ID
+        $priority = $this->request->post('priority');         //优先级
+        $categoryId = $this->request->post('categoryId');     //分类ID
+        
+        if (empty($name) || empty($description) || empty($imageUrl) || !is_numeric($adminId) || !is_numeric($priority) || !is_numeric($categoryId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        
+        Db::table('tb_forum_category')
+            ->insert([
+                'name'        => $name,
+                'description' => $description,
+                'image_url'   => $imageUrl,
+                'admin_id'    => $adminId,
+                'priority'    => $priority,
+                'category_id' => $categoryId,  // 这里是分类ID
+                'created_at'  => time(),
+                'updated_at'  => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //编辑论坛版块
+    public function editForumCategory()
+    {
+        $id = $this->request->post('id');                     //版块ID(主键)
+        $name = $this->request->post('name');                 //版块名称
+        $description = $this->request->post('description');   //版块描述
+        $imageUrl = $this->request->post('imageUrl');         //版块图片URL
+        $adminId = $this->request->post('adminId');           //版主用户ID
+        $priority = $this->request->post('priority');         //优先级
+        $categoryId = $this->request->post('categoryId');     //分类ID
+        
+        if (!is_numeric($id) || empty($name) || empty($description) || empty($imageUrl) || !is_numeric($adminId) || !is_numeric($priority) || !is_numeric($categoryId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        
+        Db::table('tb_forum_category')
+            ->where(['id' => $id])  // 使用版块ID作为主键
+            ->update([
+                'name'        => $name,
+                'description' => $description,
+                'image_url'   => $imageUrl,
+                'admin_id'    => $adminId,
+                'priority'    => $priority,
+                'category_id' => $categoryId,  // 这里是分类ID
+                'updated_at'  => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //移除版块
+    public function removeForumCategory()
+    {
+        $categoryId = $this->request->post('id');     //版块ID
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $forumCategory = Db::table('tb_forum_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$forumCategory) {
+            $this->fail(501, '找不到categoryId对应的版块');
+        }
+        Db::table('tb_forum_category')
+            ->where(['id' => $categoryId])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //获取单个对应ID论坛版块信息
+    public function getForumCategory()
+    {
+        $categoryId = $this->request->post('categoryId');     //版块ID
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $forumCategory = Db::table('tb_forum_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$forumCategory) {
+            $this->fail(501, '找不到categoryId对应的版块');
+        }
+        $this->success('success', $forumCategory);
+    }
+
+    // 获取所有辅助列表
+    public function getFuzhuList()
+    {
+        $page = $this->request->post('page');   // 页数
+        $list = $this->request->post('list');   // 每页记录数
+        $key = $this->request->post('key');     // 搜索关键字
+        if(!is_numeric($page)|| !is_numeric($list)) {
+            $this->fail(500, '参数校验错误!');
+        }
+        $query = Db::table('tb_fuzhu');
+        if ($key) {
+            $query->whereLike('title', '%'.$key.'%');
+        }
+        $data = $query->paginate([
+            'page' => $page,
+            'list_rows' => $list
+        ]);
+        $this->success('success', $data);
+    }
+
+    // 新增辅助
+    public function addFuzhu()
+    {
+        $title = $this->request->post('title');     // 辅助名称
+        $image_url = $this->request->post('image_url'); // 图片Url地址
+        $version = $this->request->post('version'); // 辅助版本
+        $file_size = $this->request->post('file_size'); // 辅助文件大小(字节)
+        $language = $this->request->post('language'); // 辅助语言
+        $game = $this->request->post('game'); // 游戏名称
+        $type = $this->request->post('type'); // 授权类型 0:免费辅助 1:收费辅助
+        $priority = $this->request->post('priority'); // 排序优先级,越小越靠前原则
+        $rate = $this->request->post('rate'); // 评分星星数,最多5分
+        $description = $this->request->post('description'); // 辅助详细描述
+        $download_url = $this->request->post('download_url'); // 下载地址URL
+        // if (!$title || !$image_url || !$version || !$file_size || $language || $game || $type || $priority || $rate || $description || $download_url) {
+        //     $this->fail(500, '请检查传参!');
+        // }
+        
+        // if (  !$language || !$game ||   !$priority || !$rate || !$description || !$download_url) {
+        //     $this->fail(5001, '请检查传参!');
+        // }
+        
+        if (!$title || !$image_url || !$version || !$file_size || !$language || !$game ||  !$priority || !$rate || !$description || !$download_url) {
+            $this->fail(500, '请检查传参!');
+        }
+        // 校验通过 插入数据库
+        Db::table('tb_fuzhu')->insert([
+            'title' => $title,
+            'image_url' => $image_url,
+            'version' => $version,
+            'file_size' => $file_size,
+            'language' => $language,
+            'game' => $game,
+            'type' => $type,
+            'priority' => $priority,
+            'rate' => $rate,
+            'description' => $description,
+            'download_url' => $download_url,
+            'created_at' => time(),
+            'updated_at' => time(),
+        ]);
+        $this->success('success', null);
+    }
+
+    // 修改辅助
+    public function updateFuzhu()
+    {
+        $id = $this->request->post('id');   // 辅助ID
+        $title = $this->request->post('title');     // 辅助名称
+        $image_url = $this->request->post('image_url'); // 图片Url地址
+        $version = $this->request->post('version'); // 辅助版本
+        $file_size = $this->request->post('file_size'); // 辅助文件大小(字节)
+        $language = $this->request->post('language'); // 辅助语言
+        $game = $this->request->post('game'); // 游戏名称
+        $type = $this->request->post('type'); // 授权类型 0:免费辅助 1:收费辅助
+        $priority = $this->request->post('priority'); // 排序优先级,越小越靠前原则
+        $rate = $this->request->post('rate'); // 评分星星数,最多5分
+        $description = $this->request->post('description'); // 辅助详细描述
+        $download_url = $this->request->post('download_url'); // 下载地址URL
+        if (!$id || !$title || !$image_url || !$version || !$file_size || !$language || !$game ||  !$priority || !$rate || !$description || !$download_url) {
+            $this->fail(500, '请检查传参!');
+        }
+        // 校验通过 插入数据库
+        Db::table('tb_fuzhu')
+            ->where('id', $id)
+            ->update([
+                'title' => $title,
+                'image_url' => $image_url,
+                'version' => $version,
+                'file_size' => $file_size,
+                'language' => $language,
+                'game' => $game,
+                'type' => $type,
+                'priority' => $priority,
+                'rate' => $rate,
+                'description' => $description,
+                'download_url' => $download_url,
+                'created_at' => time(),
+                'updated_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+
+    // 删除辅助
+    public function deleteFuzhu()
+    {
+        $id = $this->request->post('id'); // 辅助ID
+        if (!$id) {
+            $this->fail(500, '缺少参数id!');
+        }
+        $deleted = Db::table('tb_fuzhu')->where('id', $id)->delete();
+        if ($deleted) {
+            $this->success('删除成功', null);
+        } else {
+            $this->fail(500, '删除失败,未找到对应记录');
+        }
+    }
+    
+    //获取辅助下载记录,需要支持分页
+    public function getFuzhuDownload()
+    {
+        $page = $this->request->post('page');   // 当前页数
+        $list = $this->request->post('list');   // 每页记录数
+        $fuzhuId = $this->request->post('fuzhuId'); //辅助ID
+        if (!is_numeric($fuzhuId)) {
+            $this->fail(500, 'fuzhuId参数校验错误');
+        }
+        $data = Db::table('tb_fuzhu_download')
+            ->alias('tfd')
+            ->where(['tfd.fuzhu_id' => $fuzhuId])
+            ->order('tfd.created_at DESC')
+            ->join('tb_user tu', 'tfd.user_id = tu.id', 'LEFT')
+            ->field('tfd.*, tu.username, tu.avatar')
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $list
+            ]);
+        // 处理头像数据
+        $items = $data->getCollection()->toArray();
+        if ($items) {
+            foreach($items as &$item) {
+                if (!empty($item['avatar'])) {
+                    $avatarParts = explode('//', $item['avatar']);
+                    if (count($avatarParts) > 1) {
+                        $avatarId = intval($avatarParts[1]);
+                        $avatar = Db::table('tb_system_avatar')
+                            ->where(['id' => $avatarId])
+                            ->find();
+                        $item['avatar'] = $avatar['image_url'] ?? '';
+                    }
+                }
+            }
+            // 将处理后的数据重新设置到分页对象中
+            $data->setCollection(collect($items));
+        }
+        $this->success('success', $data);
+    }
+
+    /**
+     * 获取全局配置
+     */
+    public function getGlobalConfig()
+    {
+        $page = $this->request->post('page',1);   // 当前页数
+        $list = $this->request->post('list',10);   // 每页记录数
+        $key = $this->request->post('key');     // 搜索关键词 不传则不搜索
+        if (!is_numeric($page) ||!is_numeric($list)) {
+            $this->fail(500, '参数错误!');
+        }
+        $query = Db::table('tb_global_config');
+        if($key){
+            $query->whereLike('key', '%' . $key . '%');
+        }
+        $data = $query->paginate([
+                'page' => $page,
+                'list_rows' => $list
+            ]);
+        $this->success('success', $data);
+    }
+
+    /**
+     * 新增全局配置
+     */
+    public function addGlobalConfig()
+    {
+        $key = $this->request->post('key');     // 配置名称
+        $value = $this->request->post('value'); // 配置值
+        $description = $this->request->post('description'); // 配置说明
+        if(!$key || !$value) {
+            $this->fail(500, '参数校验错误!');
+        }
+        Db::table('tb_global_config')
+            ->insert([
+                'key' => $key,
+                'value' => $value,
+                'description' => $description,
+                'create_at' => time()
+            ]);
+        $this->success('success', null);
+    }
+
+    /**
+     * 修改全局配置
+     */
+    public function updateGlobalConfig()
+    {
+        $id = $this->request->post('id');                 // 主键ID
+        $key = $this->request->post('key');               // 配置名称
+        $value = $this->request->post('value');           // 配置值
+        $description = $this->request->post('description'); // 配置说明
+        
+        if(!is_numeric($id)|| !$key || !$value) {
+            $this->fail(500, '参数校验错误!');
+        }
+        Db::table('tb_global_config')
+            ->where('id', $id)
+            ->update([
+                'key' => $key,
+                'value' => $value,
+                'description' => $description,
+                'update_at' => time()
+            ]);
+        $this->success('success', null);
+    }
+
+    /**
+     * 删除全局配置
+     */
+    public function deleteGlobalConfig()
+    {
+        $id = $this->request->post('id');       // 主键ID
+        if(!is_numeric($id)) {
+            $this->fail(500, '参数校验错误!');
+        }
+        Db::table('tb_global_config')
+            ->where('id', $id)
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    
+    
+    public function getFeedBack()
+    {
+        $page = $this->request->post('page',1);   // 当前页数
+        $list = $this->request->post('list',10);   // 每页记录数
+        $key = $this->request->post('key');     // 搜索关键词 不传则不搜索
+        if (!is_numeric($page) ||!is_numeric($list)) {
+            $this->fail(500, '参数错误!');
+        }
+        $query = Db::table('tb_feedback');
+        if($key){
+            $query->whereLike('key', '%' . $key . '%');
+        }
+        $data = $query->paginate([
+                'page' => $page,
+                'list_rows' => $list
+            ]);
+        $this->success('success', $data);
+    }
+    
+    
+    
+    
+    public function getUserBug()
+    {
+        $page = $this->request->post('page',1);   // 当前页数
+        $list = $this->request->post('list',10);   // 每页记录数
+        $key = $this->request->post('key');     // 搜索关键词 不传则不搜索
+        if (!is_numeric($page) ||!is_numeric($list)) {
+            $this->fail(500, '参数错误!');
+        }
+        $query = Db::table('tb_user_bug');
+        if($key){
+            $query->whereLike('key', '%' . $key . '%');
+        }
+        $data = $query->paginate([
+                'page' => $page,
+                'list_rows' => $list
+            ]);
+        $this->success('success', $data);
+    }
+    
+    public function getThreadList()
+    {
+        $page = $this->request->post('page', 1);   // 当前页数
+        $list = $this->request->post('limit', 10);   // 每页记录数
+        $key = $this->request->post('key');     // 搜索关键词
+        $category_id = $this->request->post('category_id'); // 板块ID
+        $user_id = $this->request->post('user_id'); // 用户ID
+        $start_date = $this->request->post('start_date'); // 开始日期
+        $end_date = $this->request->post('end_date'); // 结束日期
+        
+        if (!is_numeric($page) || !is_numeric($list)) {
+            $this->fail(500, '参数错误!');
+        }
+        
+        $query = Db::table('tb_thread')
+            ->alias('t')
+            ->leftJoin('tb_forum_category fc', 't.category_id = fc.id')
+            ->field('t.*, fc.name as category_name');
+        
+        if ($key) {
+            $query->whereLike('t.title', '%' . $key . '%')
+                  ->whereOrLike('t.content', '%' . $key . '%');
+        }
+        
+        if ($category_id) {
+            $query->where('t.category_id', $category_id);
+        }
+        
+        if ($user_id) {
+            $query->where('t.user_id', $user_id);
+        }
+        
+        if ($start_date && $end_date) {
+            $query->whereBetween('t.created_at', [strtotime($start_date), strtotime($end_date) + 86399]);
+        }
+        
+        $data = $query->paginate([
+            'page' => $page,
+            'list_rows' => $list
+        ]);
+        
+        $this->success('success', $data);
+    }
+    
+    public function deleteThreadList()
+    {
+        $id = $this->request->post('id');       // 主键ID
+        if(!is_numeric($id)) {
+            $this->fail(500, '参数校验错误!');
+        }
+        
+        $result = Db::table('tb_thread')
+            ->where('id', $id)
+            ->delete();
+            
+        if ($result) {
+            $this->success('删除成功', null);
+        } else {
+            $this->fail(500, '删除失败');
+        }
+    }
+    
+    public function getForumCategories()
+    {
+        $categories = Db::table('tb_forum_category')
+            ->field('id, name')
+            ->select()
+            ->toArray();
+            
+        $this->success('success', $categories);
+    }
+    
+    
+        
+    // public function getOrderList()
+    // {
+    //     $page = $this->request->post('page',1);   // 当前页数
+    //     $list = $this->request->post('list',10);   // 每页记录数
+    //     $key = $this->request->post('key');     // 搜索关键词 不传则不搜索
+    //     if (!is_numeric($page) ||!is_numeric($list)) {
+    //         $this->fail(500, '参数错误!');
+    //     }
+    //     $query = Db::table('tb_user_order');
+    //     if($key){
+    //         $query->whereLike('key', '%' . $key . '%');
+    //     }
+    //     $data = $query->paginate([
+    //             'page' => $page,
+    //             'list_rows' => $list
+    //         ]);
+    //     $this->success('success', $data);
+    // }
+    
+    
+    
+    
+    
+    
+}

+ 100 - 0
app/controller/Authorize.php
View File

@@ -0,0 +1,100 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+use think\facade\Log;
+
+
+//授权控制器
+class Authorize extends BaseController
+{
+    protected $noNeedLogin = [];
+    
+    private $secretKey = 'E4skfomP*Pz4MOUm'; //用于计算签名的密钥key
+    
+    //绑定机器,需要购买游戏后绑定
+    //签名:md5(machineCode+gameId+secretKey)
+    public function bindMachine()
+    {
+        $machineCode = $this->request->post('machineCode');
+        $gameId = $this->request->post('gameId');
+        $sign = $this->request->post('sign');
+        if (empty($machineCode) || !is_numeric($gameId) || empty($sign)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $comparedSign = md5($machineCode . $gameId . $this->secretKey);
+        if ($sign != $comparedSign) {
+            $this->fail(504, 'sign签名校验错误');
+        }
+        $user = $this->getUser();
+        //判断gameId是否存在
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(502, 'gameId不存在');
+        }
+        //判断是否已经购买过此游戏
+        $userGame = Db::table('tb_user_game')
+            ->where(['user_id' => $user->user_id])
+            ->where(['game_id' => $gameId])
+            ->find();
+        if (!$userGame) {
+            $this->fail(503, '未购买过此游戏,无法绑定机器');
+        }
+        $gameMachine = Db::table('tb_game_machine')
+            ->where(['game_id' => $gameId])
+            ->where(['user_id' => $user->user_id])
+            ->find();
+        if ($gameMachine) {
+            $this->fail(501, '已经绑定过此款类型游戏了');
+        }
+        Db::table('tb_game_machine')
+            ->insert([
+                'user_id'      => $user->user_id,
+                'game_id'      => $gameId,
+                'machine_code' => $machineCode,
+                'created_at'   => time(),
+                'expired_at'   => time() + 60 * 60 * 24 * 30 * 12 * 3,
+            ]);
+        //写绑定记录
+        Db::table('tb_game_bind_record')
+            ->insert([
+                'user_id'      => $user->user_id,
+                'game_id'      => $gameId,
+                'machine_code' => $machineCode,
+                'created_at'   => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //验证机器码
+    //签名:md5(machineCode+gameId+secretKey)
+    public function verifyMachine()
+    {
+        $machineCode = $this->request->post('machineCode');
+        $gameId = $this->request->post('gameId');
+        $sign = $this->request->post('sign');
+        if (empty($machineCode) || !is_numeric($gameId) || empty($sign)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $comparedSign = md5($machineCode . $gameId . $this->secretKey);
+        if ($sign != $comparedSign) {
+            $this->fail(503, 'sign签名校验错误');
+        }
+        $user = $this->getUser();
+        $data = Db::table('tb_game_machine')
+            ->where(['user_id'      => $user->user_id])
+            ->where(['game_id'      => $gameId])
+            ->where(['machine_code' => $machineCode])
+            ->find();
+        if (!$data) {
+            $this->fail(501, '验证失败,没有机器绑定记录');
+        }
+        if ($data['expired_at'] < time()) {
+            $this->fail(502, '验证失败,机器绑定已过期');
+        }
+        $this->success('success', null);
+    }
+}

+ 151 - 0
app/controller/Common.php
View File

@@ -0,0 +1,151 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//公共控制器
+class Common extends BaseController
+{
+    protected $noNeedLogin = ['statisticInfo', 'getUserProfile', 'visit', 'viewFriendList'];
+    
+    //虚假初始会员数量
+    private $falseUserCount = 1000;
+    
+    //获取首页统计信息
+    public function statisticInfo()
+    {
+        //今日新上架游戏商品数
+        $todayGameCount = Db::table('tb_game')
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->count();
+        //昨日上架游戏商品数
+        $yesterdayGameCount = Db::table('tb_game')
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00') - 60 * 60 * 24)
+            ->where('created_at', '<', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->count();
+        //全部上架游戏商品数
+        $allGameCount = Db::table('tb_game')->count();
+        //会员数量
+        $userCount = Db::table('tb_user')->count() + $this->falseUserCount;
+        //欢迎新会员
+        $user = Db::table('tb_user')
+            ->order('created_at DESC')
+            ->find();
+        $this->success('success', [
+            'todayGameCount'     => $todayGameCount,      //今日新上架游戏数量
+            'yesterdayGameCount' => $yesterdayGameCount,  //昨日上架游戏数量
+            'allGameCount'       => $allGameCount,        //所有游戏数量
+            'userCount'          => $userCount,           //总用户数量
+            'newUser'            => [                     //新用户信息
+                'userId'   => !isset($user['id']) ? null : $user['id'],    //用户ID,如果站点目前没人注册会员返回null
+                'username' => !isset($user['username']) ? null : $user['username'],  //用户名,如果站点目前没人注册会员返回null
+            ],
+        ]);
+    }
+    
+    //获取用户资料
+    public function getUserProfile()
+    {
+        $userId = $this->request->post('userId'); //用户ID
+        if (!is_numeric($userId)) {
+            $this->fail(500, 'userId参数校验错误');
+        }
+        $user = Db::table('tb_user')
+            ->where(['id' => $userId])
+            ->find();
+        if (!$user) {
+            $this->fail(501, '找不到userId对应的用户资料');
+        }
+        //解析获取头像url
+        if (str_starts_with($user['avatar'], 'system://')) {
+            $data = explode('//', $user['avatar']);
+            $avatarId = intval($data[1]);
+            $avatar = Db::table('tb_system_avatar')
+                ->where(['id' => $avatarId])
+                ->find();
+            $user['avatar'] = $avatar['image_url'];
+        }
+        //获取好友数量
+        $friendCount = Db::table('tb_friend')
+            ->where(['user_id' => $userId])
+            ->where(['state'   => 1])  //状态必须是已通过
+            ->count();
+        unset($user['password']);
+        unset($user['balance']);
+        //好友数量
+        $user['friend_count'] = $friendCount;
+        $userGroupList = Db::table('tb_user_group')
+            ->order('exp DESC')
+            ->select();
+        $userGroupName = '新手上路';
+        foreach ($userGroupList as $userGroup) {
+            if ($user['exp'] >= $userGroup['exp']) {
+                $userGroupName = $userGroup['name'];
+                break;
+            }
+        }
+        $user['user_group'] = $userGroupName;
+        $user['theme_count'] = Db::table('tb_thread')->where('user_id', $userId)->count();
+        $user['reply_count'] = Db::table('tb_thread_reply')->where('user_id', $userId)->count();
+        $user['user_score'] = Db::table('tb_user')->where('id', $userId)->value('score');
+        $this->success('success', $user);
+    }
+    
+    //查看好友列表
+    public function viewFriendList()
+    {
+        $userId = $this->request->post('userId'); //用户ID
+        if (!is_numeric($userId)) {
+            $this->fail(500, 'userId参数校验错误');
+        }
+        $map1 = ['user_id', '=', $userId];
+        $map2 = ['friend_id', '=', $userId];
+        $friends = Db::table('tb_friend')
+            ->where(['state' => 1])  //状态必须是已通过
+            ->where(function ($query) use($map1, $map2) {
+                $query->whereOr([$map1, $map2]); 
+            })
+            ->select();
+        $friendsData = [];
+        foreach ($friends as $friend) {
+            $friendId = 0;
+            //好友是双向的,判断是否当前userId
+            if ($friend['user_id'] == $userId) {
+                $friendId = $friend['friend_id'];
+            } else {
+                $friendId = $friend['user_id'];
+            }
+            $user = Db::table('tb_user')
+                ->where(['id' => $friendId])
+                ->find();
+            unset($user['password']);
+            unset($user['balance']);
+            $friendsData[] = $user;
+        }
+        $this->success('success', [
+            'friendCount' => count($friendsData),  //好友列表数量
+            'data'        => $friendsData,         //返回所有好友的user数据
+        ]);
+    }
+    
+    //创建网站访问记录
+    public function visit()
+    {
+        //如果有登录需要POST方式传递token
+        $token = $this->request->post('token');
+        $userToken = Db::table('tb_user_token')
+            ->where(['token' => $token])
+            ->where('expired_at', '<', time())
+            ->find();
+        Db::table('tb_website_visit')
+            ->insert([
+                'ip_address' => $this->request->ip(),
+                'is_user'    => !is_null($userToken) && !empty($token) ? 1 : 0,                      //是否注册用户1:是  0:否
+                'user_id'    => !is_null($userToken) && !empty($token) ? $userToken['user_id'] : 0,  //如果是注册用户写用户ID,非注册用户写0
+                'user_agent' => $this->request->header('user-agent'),
+                'created_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+}

+ 101 - 0
app/controller/Email.php
View File

@@ -0,0 +1,101 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+use PHPMailer\PHPMailer\PHPMailer;
+use PHPMailer\PHPMailer\Exception;
+
+//邮件控制器
+
+class Email extends BaseController
+{
+    protected $noNeedLogin = ['sendVerifyEmail'];
+
+    // 发送邮箱验证码
+    public function sendVerifyEmail() {
+        $email = $this->request->post('email'); // 邮箱地址
+        $event = $this->request->post('event' ?? 'register'); // 验证码事件
+        if (!$email || !$event) {
+            $this->fail(500, '请检查传参!');
+        }
+        // 随机验证码
+        $code = mt_rand(1000, 9999);
+        // 插入数据库
+        $row = Db::table('tb_email_captcha')->insert([
+            'email' => $email,
+            'code' => $code,
+            'event' => $event,
+            'ip' => $this->request->ip(),
+            'createtime' => time(),
+        ]);
+        if (!$row) {
+            $this->fail(500, '发送失败!请联系管理员处理!');
+        }
+        $mail = new PHPMailer(true);
+        $mail->isSMTP();
+        $mail->Host       = 'smtp.yeah.net';     // 设置 SMTP 服务器
+        $mail->SMTPAuth   = true;                   // 启用 SMTP 认证
+        $mail->Username   = 'danjiwanjia@yeah.net';    // 用户名
+        $mail->Password   = 'UMt6A8pm2KY2unCM';      // 密码
+        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // SMTP 协议类型
+        $mail->Port       = 25;                    // SMTP 端口号
+        
+        $mail->setFrom('danjiwanjia@yeah.net', '单机顽家');
+        $mail->addAddress($email); // 添加收件人
+
+        $mail->isHTML(true);                                  // Set email format to HTML
+        $mail->Subject = '单机顽家验证码';
+        $mail->Body    = '<b>您的验证码是:' . $code . ',请在5分钟内使用,答应我不要告诉别人哦!</b>';
+        $mail->AltBody = '您的验证码是:' . $code;
+        
+        $mail->send();
+        
+        $this->success('发送成功!', null);
+    }
+
+    // 内部调用 - 验证邮箱
+    public function verifyEmail($email, $code, $event = 'register')
+    {
+        if (!$email || !$event) {
+            $this->fail(500, '请检查传参!');
+        }
+        // 查询邮箱验证码
+        $query = Db::table('tb_email_captcha')
+            ->where('email', $email)
+            ->where('code', $code);
+        $email = $query->find();
+        // 校验验证码状态
+        if (!$email) {
+            // 验证码未找到或不正确
+            return false;
+        }
+        if ($email['times'] != 0 || time() - $email['createtime'] > 300) {
+            // 验证码已经被使用或超过五分钟未使用
+            return false;
+        }
+        // 通过验证 将验证码标记为已使用
+        $query->update(['times' => 1]);
+        return true;
+    }
+
+    // 用户验证邮箱
+    public function verifyUserEmail()
+    {
+        $code = $this->request->post('code');// 邮箱验证码
+        $user_id = $this->getUser()->user_id;
+        // 验证用户
+        $user = Db::table('tb_user')->where('id', $user_id)->find();
+        if (!$user) {
+            $this->fail(500, '无法找到用户信息!');
+        }
+        // 验证用户邮箱
+        $verify = $this->verifyEmail($user['email'], $code, 'verify');
+        if (!$verify) {
+            $this->fail(500, '验证码错误或已过期!');
+        }
+        // 校验通过 更新用户邮箱已认证
+        Db::table('tb_user')->where('id', $user['id'])->update(['is_email_verified' => 1]);
+        $this->success('success', null);
+    }
+}

+ 71 - 0
app/controller/Feedback.php
View File

@@ -0,0 +1,71 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//投诉反馈控制器
+class Feedback extends BaseController
+{
+    protected $noNeedLogin = [];
+
+    // 获取我的投诉与反馈
+    public function getMyFeedback()
+    {
+        $user_id = $this->getUser()->user_id;
+        if(!is_numeric($user_id)){
+            $this->fail(500, '无法获取用户信息');
+        }
+        $data = Db::table('tb_feedback')->where('user_id')->select();
+        $this->success('success', $data);
+    }
+
+    // 发起投诉与反馈
+    public function submitFeedback()
+    {
+        $content = $this->request->post('content'); // 投诉建议正文
+        $user_id = $this->getUser()->user_id;
+        if(!is_numeric($user_id)||!$content){
+            $this->fail(500, '参数校验错误!');
+        }
+        Db::table('tb_feedback')
+            ->insert([
+                'user_id'   => $user_id,
+                'content'   => $content,
+                'create_at' => time()
+            ]);
+        $this->success('success', null);
+    }
+
+    // 获取我反馈的Bug
+    public function getMyReportBug()
+    {
+        $user_id = $this->getUser()->user_id;
+        if(!is_numeric($user_id)){
+            $this->fail(500, '无法获取用户信息');
+        }
+        $data = Db::table('tb_user_bug')->where('user_id')->select();
+        $this->success('success', $data);
+    }
+
+    // 发起Bug反馈
+    public function reportBug()
+    {
+        $type = $this->request->post('type');   // 反馈类型 1单机游戏 2收费辅助
+        $game_id = $this->request->post('game_id');     // 对应游戏ID
+        $content = $this->request->post('content'); // Bug反馈正文(富文本)
+        $content = html_entity_decode($content);
+        $user_id = $this->getUser()->user_id;
+        if(!is_numeric($type)||!is_numeric($game_id)||!is_numeric($user_id)||!$content){
+            $this->fail(500, '参数校验错误!');
+        }
+        Db::table('tb_user_bug')
+            ->insert([
+                'user_id'   => $user_id,
+                'type'      => $type,
+                'content'   => $content,
+                'create_at' => time()
+            ]);
+        $this->success('success', null);
+    }
+}

+ 416 - 0
app/controller/Forum.php
View File

@@ -0,0 +1,416 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//论坛控制器
+class Forum extends BaseController
+{
+    protected $noNeedLogin = ['getCategory', 'getCategoryNotice', 'getForumCategoryData', 'getForumCategoryOnly', 'getThreads', 'getThread', 'getThreadReply', 'getCategoryData'];
+    
+    //获取版块
+    public function getCategory()
+    {
+        $data = Db::table('tb_forum_category')
+            ->order('priority ASC')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    
+    //获取版块置顶公告
+    public function getCategoryNotice()
+    {
+        $categoryId = $this->request->post('categoryId');    //版块ID
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $data = Db::table('tb_forum_category_notice')
+            ->where(['category_id' => $categoryId])
+            ->find();
+        if (!$data) {
+            $this->fail(501, '找不到categoryId对应的版块公告');
+        }
+        $this->success('success', $data);
+    }
+    
+    //获取版块数据
+    public function getCategoryData()
+    {
+        $categoryId = $this->request->post('categoryId');    //版块ID
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $category = Db::table('tb_forum_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$category) {
+            $this->fail(501, '版块ID不存在');
+        }
+        $today = Db::table('tb_thread')
+            ->where(['category_id' => $categoryId])
+            ->where('created_at', '>', strtotime(date('Y-m-d') . ' 00:00:00'))
+            ->count();
+        $topic = Db::table('tb_thread')
+            ->where(['category_id' => $categoryId])
+            ->count();
+        
+        $admin = Db::table('tb_user')
+            ->field('id,username,avatar')
+            ->where(['id' => $category['admin_id']])
+            ->find();
+        //解析获取头像url
+        if (str_starts_with($admin['avatar'], 'system://')) {
+            $data = explode('//', $admin['avatar']);
+            $avatarId = intval($data[1]);
+            $avatar = Db::table('tb_system_avatar')
+                ->where(['id' => $avatarId])
+                ->find();
+            $admin['avatar'] = $avatar['image_url'];
+        }
+        $this->success('success', [
+            'name'  => $category['name'],  //版块名称
+            'today' => $today,             //今日
+            'topic' => $topic,             //主题
+            'admin' => $admin,             //版主信息
+        ]);
+    }
+    
+    
+    //获取帖子分类带数据
+    public function getForumCategoryData()
+    {
+        $categoryId = $this->request->post('categoryId');    //版块ID
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $threadCategory = Db::table('tb_forum_thread_category')
+            ->where(['category_id' => $categoryId])
+            ->select();
+        $data = [];
+        foreach ($threadCategory as $categoryData) {
+            $threadsCount = Db::table('tb_thread')
+                ->where(['category_id'     => $categoryId])
+                ->where(['thread_category' => $categoryData['id']])
+                ->count();
+            $data[] = [
+                'threadCategoryId' => $categoryData['id'],        //分类ID
+                'threadCategoryTitle' => $categoryData['title'],  //帖子分类标题
+                'threadsCount'        => $threadsCount,           //帖子数量
+            ];
+        }
+        $this->success('success', $data);
+    }
+    
+    
+    //仅获取帖子分类
+    public function getForumCategoryOnly()
+    {
+        $categoryId = $this->request->post('categoryId');   //版块ID
+        if (!is_numeric($categoryId)) {
+            $this->fail(500, 'categoryId参数校验错误');
+        }
+        $threadCategory = Db::table('tb_forum_thread_category')
+            ->where(['category_id' => $categoryId])
+            ->select();
+        $this->success('success', $threadCategory);
+    }
+    
+    //获取版块下帖子列表
+    public function getThreads()
+    {
+        $page = $this->request->post('page');    // 页码
+        $list = $this->request->post('list');   // 每页记录数
+        $categoryId = $this->request->post('categoryId');   //版块ID 999代表全部
+        $thread_category = $this->request->post('thread_category'); // 板块下分类ID
+        if (!is_numeric($page)|| !is_numeric($list)|| !is_numeric($categoryId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $query = Db::table('tb_thread')
+            ->alias('tt')
+            ->order('tt.created_at DESC')
+            ->join('tb_user tu', 'tt.user_id = tu.id')
+            ->field('tt.*, tu.username, tu.avatar');
+        if($categoryId == 999) {
+            // 999的情况下不做任何筛选
+        } else {
+            $query->where(['tt.category_id' => $categoryId]);
+        }
+        if ($thread_category) {
+            $query->where(['tt.thread_category' => $thread_category]);
+        }
+        $data = $query->paginate([
+            'page' => $page,
+            'list_rows' => $list
+        ]);
+        // // 处理头像数据
+        // $items = $data->getCollection()->toArray();
+        // foreach($items as &$item) {
+        //     if (isset($item['avatar'])) {
+        //         $item['avatar'] = $this->getUserAvatar($item['avatar']);
+        //     }
+        // }
+        // unset($item); // 释放引用
+        // // 重新设置处理后的数据
+        // $data->setCollection(collect($items));
+        $this->success('success', $data);
+        }
+    //获取帖子内容,前端需要做下判断:如果登录了header需要传token,没登录就不传
+    public function getThread()
+    {
+        $token = $this->request->header('token');
+        $threadId = $this->request->post('threadId');
+        if (!is_numeric($threadId)) {
+            $this->fail(500, 'threadId参数校验错误');
+        }
+        $thread = Db::table('tb_thread')
+            ->alias('tt')
+            ->where(['tt.id' => $threadId])
+            ->join('tb_user tu', 'tt.user_id = tu.id')
+            ->field('tt.*, tu.username, tu.avatar')
+            ->find();
+        if (!$thread) {
+            $this->fail(501, '找不到threadId对应的帖子');
+        }
+        $thread['avatar'] = $this->getUserAvatar($thread['avatar']);
+        $userId = 0;
+        $isUser = 0;
+        if (!empty($token)) {
+            $userToken = Db::table('tb_user_token')
+                ->where(['token' => $token])
+                ->find();
+            if ($userToken && $userToken['expired_at'] > time()) {
+                $userId = $userToken['user_id'];
+                $isUser = 1;
+            }
+        }
+        Db::table('tb_thread_visit')
+            ->insert([
+                'user_id'    => $userId,
+                'is_user'    => $isUser,
+                'created_at' => time(),
+            ]);
+        Db::table('tb_thread')
+            ->where(['id' => $threadId])
+            ->update([
+                'browse' => $thread['browse'] + 1,
+            ]);
+        $thread['theme_count'] = Db::table('tb_thread')->where('user_id', $userId)->count();
+        $thread['theme_count'] = Db::table('tb_thread_reply')->where('user_id', $userId)->count();
+        $thread['user_score'] = Db::table('tb_user')->where('id', $userId)->value('score');
+        $this->success('success', $thread);
+    }
+    
+    //获取自己的帖子内容
+    public function getSelfThread()
+    {
+        $threadId = $this->request->post('threadId');
+        if (!is_numeric($threadId)) {
+            $this->fail(500, 'threadId参数校验错误');
+        }
+        $thread = Db::table('tb_thread')
+            ->where(['id'      => $threadId])
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->find();
+        if (!$thread) {
+            $this->fail(501, '找不到threadId对应的帖子');
+        }
+        $this->success('success', $thread);
+    }
+    
+    //获取自己的回复内容
+    public function getSelfReply()
+    {
+        $replyId = $this->request->post('replyId');
+        if (!is_numeric($replyId)) {
+            $this->fail(500, 'replyId参数校验错误');
+        }
+        $reply = Db::table('tb_thread_reply')
+            ->where(['id'      => $replyId])
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->find();
+        if (!$reply) {
+            $this->fail(501, '找不到replyId对应的帖子');
+        }
+        $this->success('success', $reply);
+    }
+    
+    //发表帖子
+    public function postThread()
+    {
+        $categoryId = $this->request->post('categoryId');              //版块ID
+        $title = $this->request->post('title');                        //帖子标题
+        $threadCategoryId = $this->request->post('threadCategoryId');  //帖子分类ID
+        $content = $this->request->post('content');                    //帖子内容
+        if (!is_numeric($categoryId) || empty($title) || empty($content) || !is_numeric($threadCategoryId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        //版块ID是否存在
+        $forumCategory = Db::table('tb_forum_category')
+            ->where(['id' => $categoryId])
+            ->find();
+        if (!$forumCategory) {
+            $this->fail(501, '对应的版块ID不存在');
+        }
+        //帖子分类ID是否存在
+        $threadCategory = Db::table('tb_forum_thread_category')
+            ->where(['id' => $threadCategoryId])
+            ->find();
+        if (!$threadCategory) {
+            $this->fail(502, '帖子分类ID不存在');
+        }
+        Db::table('tb_thread')
+            ->insert([
+                'category_id'     => $categoryId,
+                'thread_category' => $threadCategoryId,
+                'user_id'         => $this->getUser()->user_id,
+                'title'           => $title,
+                'content'         => $content,
+                'browse'          => 0,
+                'created_at'      => time(),
+                'updated_at'      => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //编辑帖子
+    public function editThread()
+    {
+        $threadId = $this->request->post('threadId');                  //帖子ID
+        $title = $this->request->post('title');                        //帖子标题
+        $threadCategoryId = $this->request->post('threadCategoryId');  //帖子分类ID
+        $content = $this->request->post('content');                    //帖子内容
+        if (!is_numeric($threadId) || !is_numeric($categoryId) || empty($title) || empty($content) || !is_numeric($threadCategory)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $selfThread = Db::table('tb_thread')
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->where(['id'      => $threadId])
+            ->find();
+        if (!$selfThread) {
+            $this->fail(501, '只能编辑自己的帖子');
+        }
+        Db::table('tb_thread')
+            ->where(['id'      => $threadId])
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->update([
+                'thread_category' => $threadCategoryId,
+                'title'           => $title,
+                'content'         => $content,
+                'updated_at'      => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //发表回复
+    public function postReply()
+    {
+        $threadId = $this->request->post('threadId');  //帖子ID 
+        $content = $this->request->post('content');    //回复内容
+        if (!is_numeric($threadId) || empty($content)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $thread = Db::table('tb_thread')
+            ->where(['id' => $threadId])
+            ->find();
+        if (!$thread) {
+            $this->fail(501, '帖子ID不存在');
+        }
+        Db::table('tb_thread_reply')
+            ->insert([
+                'user_id'    => $this->getUser()->user_id,
+                'thread_id'  => $threadId,
+                'content'    => $content,
+                'created_at' => time(),
+                'updated_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //编辑回复 
+    public function editReply()
+    {
+        $replyId = $this->request->post('replyId');
+        $content = $this->request->post('content');
+        if (!is_numeric($replyId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $reply = Db::table('tb_thread_reply')
+            ->where(['id'      => $replyId])
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->find();
+        if (!$reply) {
+            $this->fail(501, '只能编辑自己的帖子回复');
+        }
+        Db::table('tb_thread_reply')
+            ->where(['id'      => $replyId])
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->update([
+                'content'    => $content,
+                'updated_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    
+    //获取帖子回复,需要支持分页
+    public function getThreadReply()
+    {
+        $page = $this->request->post('page');   // 当前页数
+        $list = $this->request->post('list');   // 每页记录数
+        $threadId = $this->request->post('threadId');  //帖子ID 
+        if (!is_numeric($threadId)) {
+            $this->fail(500, 'threadId参数校验错误');
+        }
+        $data = Db::table('tb_thread_reply')
+            ->where(['thread_id' => $threadId])
+            ->order('created_at ASC')
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $list
+            ]);
+        $this->success('success', $data);
+    }
+    
+    
+    //上传图片,图片文件不能超过2MB
+    public function uploadImage()
+    {
+        $file = $this->request->file('file');
+        if (empty($file)) {
+            $this->fail(500, '上传的图片文件不能为空');
+        }
+        // 使用验证器验证上传的文件
+        validate(['file' => [
+            // 限制文件大小(单位b),这里限制为2M
+            'fileSize' => 2 * 1024 * 1024,
+            // 限制文件后缀,多个后缀以英文逗号分割
+            'fileExt'  => 'jpg,jpeg,png,gif,bmp,webp',
+        ]])->check(['file' => $file]);
+        $saveName = \think\facade\Filesystem::disk('public')->putFile('images', $file);
+        $this->success('success', [
+            'filePath' => $this->request->scheme() . '://' . $this->request->host() . '/storage/' . $saveName,
+        ]);
+    }
+    
+    //上传附件,单个附件不得超过50MB
+    public function uploadAttachment()
+    {
+        $file = $this->request->file('file');
+        if (empty($file)) {
+            $this->fail(500, '上传的图片文件不能为空');
+        }
+        // 使用验证器验证上传的文件
+        validate(['file' => [
+            // 限制文件大小(单位b),这里限制为20M
+            'fileSize' => 50 * 1024 * 1024,
+            // 限制文件后缀,多个后缀以英文逗号分割
+            'fileExt'  => 'rar,zip,tar.gz,gz,7z,txt',
+        ]])->check(['file' => $file]);
+        $saveName = \think\facade\Filesystem::disk('public')->putFile('attachments', $file);
+        $this->success('success', [
+            'filePath' => $this->request->scheme() . '://' . $this->request->host() . '/storage/' . $saveName,
+        ]);
+    }
+}

+ 141 - 0
app/controller/Friend.php
View File

@@ -0,0 +1,141 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//好友控制器
+class Friend extends BaseController
+{
+    
+    //获取我下面的好友列表
+    public function getMyFriends()
+    {
+        $userId = $this->getUser()->user_id; //用户ID
+        $map1 = ['user_id', '=', $userId];
+        $map2 = ['friend_id', '=', $userId];
+        $friends = Db::table('tb_friend')
+            ->where(['state'       => 1])  //状态必须是已通过
+            ->where(function($query) use($map1, $map2) {
+                $query->whereOr([$map1, $map2]);
+            })
+            ->select();
+        $friendsData = [];
+        foreach ($friends as $friend) {
+            $friendId = 0;
+            //好友是双向的,先排除自己
+            if ($friend['user_id'] == $userId) {
+                $firendId = $friend['friend_id'];
+            } else {
+                $friendId = $friend['user_id'];
+            }
+            $user = Db::table('tb_user')
+                ->where(['id' => $friendId])
+                ->find();
+            unset($user['password']);
+            unset($user['balance']);
+            $friendsData[] = $user;
+        }
+        $this->success('success', [
+            'friendCount' => count($friendsData),  //好友列表数量
+            'data'        => $friendsData,         //返回所有好友的user数据
+        ]);
+    }
+    
+    //添加好友
+    public function addFriend()
+    {
+        $friendId = $this->request->post('friendId');  //需要加好友的用户ID
+        $msg = $this->request->post('msg');            //加好友验证消息
+        if (empty($msg) || !is_numeric($friendId)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = $this->getUser();
+        //先判断申请有没有到期,否则不能重复申请加好友
+        $friend = Db::table('tb_friend')
+            ->where(['user_id'   => $user->user_id])
+            ->where(['friend_id' => $friendId])
+            ->find();
+        if ($friend && $friend['state'] == 1) {
+            $this->fail(501, '对方已经是您的好友。');
+        }
+        if ($friend && $friend['state'] == 2) {
+            $this->fail(503, '对方已拒绝,暂时还无法加好友。');
+        }
+        if ($friend && $friend['expired_at'] > time()) {
+            $this->fail(502, '对方还未同意,请耐心等待');
+        }
+        $requestId = 0;
+        if (!$friend) {
+            $requestId = Db::table('tb_friend')
+                ->insertGetId([
+                    'user_id'    => $user->user_id,
+                    'friend_id'  => $friendId,
+                    'state'      => 0,
+                    'msg'        => $msg,
+                    'created_at' => time(),
+                    'updated_at' => time(),
+                    'expired_at' => time() + 60 * 60 * 48,  //48小时后到期,期间内不可以重复添加
+                ]);
+        } else {
+            $requestId = $friend['id'];
+            Db::table('tb_friend')
+                ->where(['user_id'   => $user->user_id])
+                ->where(['friend_id' => $friendId])
+                ->update([
+                    'updated_at' => time(),
+                    'expired_at' => time() + 60 * 60 * 48,
+                ]);
+        }
+        $this->success('success', [
+            'requestId' => $requestId,
+        ]);
+    }
+    
+    //接受添加好友
+    public function acceptFriend()
+    {
+        $requestId = $this->request->post('requestId'); //请求ID
+        $isAccept = $this->request->post('isAccept');   //是否接受添加好友  1:接受  0:拒绝
+        if (!is_numeric($requestId) || !is_numeric($isAccept)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $isAccept = intval($isAccept);
+        if (!in_array($isAccept, [0, 1])) {
+            $this->fail(500, '参数校验错误');
+        }
+        //requestId是否存在
+        $friend = Db::table('tb_friend')
+            ->where(['friend_id' => $this->getUser()->user_id])
+            ->where(['id'        => $requestId])
+            ->find();
+        if (!$friend) {
+            $this->fail(501, 'requestId不存在');
+        }
+        //判断是否到期
+        if ($friend['expired_at'] < time()) {
+            $this->fail(502, '好友验证已经过期,无法通过');
+        }
+        Db::table('tb_friend')
+            ->where(['id'        => $requestId])
+            ->where(['friend_id' => $this->getUser()->user_id])
+            ->update([
+                'updated_at' => time(),
+                'friend_id'  => $this->getUser()->user_id,
+                'state'      => $isAccept == 1 ? 1 : 2,
+            ]);
+        $this->success('success', null);
+    }
+    
+    
+    //获取好友验证消息,过期的不再获取
+    public function getAcceptFriendMessage()
+    {
+        $data = Db::table('tb_friend')
+            ->where(['friend_id' => $this->getUser()->user_id])
+            ->where('expired_at', '<', time())
+            ->order('created_at DESC')
+            ->select();
+        $this->success('success', $data);
+    }
+}

+ 143 - 0
app/controller/Fuzhu.php
View File

@@ -0,0 +1,143 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//辅助下载控制器
+class Fuzhu extends BaseController
+{
+    protected $noNeedLogin = ['search', 'getFuzhu'];
+    
+    //搜索辅助
+    public function search()
+    {
+        $keywords = $this->request->post('keywords');  //搜索关键字
+        if (empty($keywords)) {
+            $this->fail(500, '关键字不能为空');
+        }
+        $data = Db::table('tb_fuzhu')
+            ->where('title', 'like', '%' . $keywords . '%')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    //获取辅助,需要支持分页
+    public function getFuzhu()
+    {
+        $data = Db::table('tb_fuzhu')
+            ->order('priority ASC')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    //获取辅助详细信息,需登录
+    public function detail()
+    {
+        $fuzhuId = $this->request->post('fuzhuId');   //辅助ID
+        if (!is_numeric($fuzhuId)) {
+            $this->fail(500, 'fuzhuId参数校验错误');
+        }
+        $data = Db::table('tb_fuzhu')
+            ->where(['id' => $fuzhuId])
+            ->find();
+        if (!$data) {
+            $this->fail(501, '找不到fuzhuId对应的辅助信息');
+        }
+        //投票数据赞踩百分比
+        $totalVoteCount = Db::table('tb_fuzhu_vote')
+            ->where(['fuzhu_id' => $fuzhuId])
+            ->count();
+        $totalUpCount = Db::table('tb_fuzhu_vote')
+            ->where(['fuzhu_id' => $fuzhuId])
+            ->where(['value'    => 1])
+            ->count();
+        $totalDownCount = Db::table('tb_fuzhu_vote')
+            ->where(['fuzhu_id' => $fuzhuId])
+            ->where(['value'    => 0])
+            ->count();
+        $upPercent = 0;
+        $downPercent = 0;
+        //被除数总投票次数不能为0,如果为0,投票百分比应当均为0
+        if ($totalVoteCount != 0) {
+            //需要转换为float计算保留2位小数,否则整数/整数=整数
+            $upPercent = number_format(floatval($totalUpCount) / floatval($totalVoteCount) * 100, 2);
+            $downPercent = number_format(floatval($totalDownCount) / floatval($totalVoteCount) * 100, 2);
+        }
+        $data['vote'] = [
+            'upPercent'   => $upPercent,    //赞投票百分比
+            'downPercent' => $downPercent,  //踩投票百分比
+        ];
+        //下载次数
+        $data['download_count'] = Db::table('tb_fuzhu_download')
+            ->where(['fuzhu_id' => $fuzhuId])
+            ->count();
+        $this->success('success', $data);
+    }
+    
+    //推荐的猜你喜欢辅助,按排序优先级推荐最多5个,(后面优化到用户行为推荐),需登录
+    public function recommendedFuzhu()
+    {
+        $fuzhuId = $this->request->post('fuzhuId');   //辅助ID
+        if (!is_numeric($fuzhuId)) {
+            $this->fail(500, 'fuzhuId参数校验错误');
+        }
+        $data = Db::table('tb_fuzhu')
+            ->order('priority ASC')
+            ->limit(5)
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    //赞踩投票,需登录
+    public function vote()
+    {
+        $fuzhuId = $this->request->post('fuzhuId');   //辅助ID
+        $value = $this->request->post('value');       //投票值  1:赞  0:踩
+        if (!is_numeric($fuzhuId) || !is_numeric($value)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $value = intval($value);
+        if (!in_array($value, [0, 1])) {
+            $this->fail(500, '参数校验错误');
+        }
+        $fuzhu = Db::table('tb_fuzhu')
+            ->where(['id' => $fuzhuId])
+            ->find();
+        if (!$fuzhu) {
+            $this->fail(501, '辅助ID不存在');
+        }
+        //检测是否已经投过票
+        $fuzhuVote = Db::table('tb_fuzhu_vote')
+            ->where(['fuzhu_id' => $fuzhuId])
+            ->where(['user_id'  => $this->getUser()->user_id])
+            ->find();
+        if ($fuzhuVote) {
+            $this->fail(502, '您已经投过票');
+        }
+        Db::table('tb_fuzhu_vote')
+            ->insert([
+                'user_id'    => $this->getUser()->user_id,
+                'fuzhu_id'   => $fuzhuId,
+                'value'      => $value,
+                'created_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+    
+    //下载辅助
+    public function download()
+    {
+        $gameId = $this->request->post('gameId');
+        if (!is_numeric($gameId)) {
+            $this->fail(500, 'gameId参数校验错误');
+        }
+        Db::table('tb_fuzhu_download')
+            ->insert([
+                'fuzhu_id'   => $fuzhuId,
+                'user_id'    => $this->getUser()->user_id,
+                'created_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+}

+ 316 - 0
app/controller/Game.php
View File

@@ -0,0 +1,316 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//游戏控制器
+class Game extends BaseController
+{
+    protected $noNeedLogin = ['getCategory', 'getGamesByCategory', 'getGameById', 'searchGame', 'getHotSearchKeywords', 'recommendedGames'];
+    
+    //获取游戏分类
+    public function getCategory()
+    {
+        $data = Db::table('tb_game_category')
+            ->order('priority ASC')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    //根据分类获取游戏
+    public function getGamesByCategory()
+    {
+        $categoryId = $this->request->post('categoryId');   //分类ID
+        $page = $this->request->post('page');   // 当前页码
+        $pageNum = $this->request->post('pageNum');         //每页显示的数据条数
+        if (!is_numeric($categoryId) || !is_numeric($pageNum)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $games = Db::table('tb_game')
+            ->field('id,title,image_url,description,buy_count,browse,price')
+            ->where(['category' => $categoryId])
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $pageNum
+            ]);
+        $this->success('success', $games);
+    }
+    
+    //根据游戏ID获取游戏
+    public function getGameById()
+    {
+        $gameId = $this->request->post('gameId');
+        if (!is_numeric($gameId)) {
+            $this->fail(500, 'gameId参数校验错误');
+        }
+        $data = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$data) {
+            $this->fail(500, 'gameId不存在');
+        }
+        //浏览次数+1
+        Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->update([
+                'browse' => $data['browse'] + 1,
+            ]);
+        unset($data['download_url']);
+        unset($data['extracted_code']);
+        $this->success('success', $data);
+    }
+    
+    //购买游戏
+    public function buyGame()
+    {
+        $gameId = $this->request->post('gameId');
+        if (!is_numeric($gameId)) {
+            $this->fail(500, 'gameId参数校验错误');
+        }
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(501, 'gameId不存在');
+        }
+        $user = Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->find();
+        if ($user['balance'] < $game['price']) {
+            $this->fail(502, '余额不足无法购买');
+        }
+        //避免重复购买同一款游戏
+        $userGame = Db::table('tb_user_game')
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->where(['game_id' => $gameId])
+            ->find();
+        if ($userGame) {
+            $this->fail(503, '您已经购买过此款游戏了');
+        }
+        //减少余额
+        Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->update(['balance' => $user['balance'] - $game['price']]);
+        //购买到记录
+        Db::table('tb_user_game')
+            ->insert([
+                'user_id'    => $this->getUser()->user_id,
+                'game_id'    => $gameId,
+                'price'      => $game['price'],
+                'created_at' => time(),
+                'channel'    => 1,  //站内直购
+            ]);
+        $this->success('success', null);
+    }
+    
+    //获取我已经购买的游戏,包括cdkey兑换
+    public function getMyBoughtGames()
+    {
+        $page = $this->request->post('page');         //页码
+        $pageNum = $this->request->post('pageNum');         //每页显示的数据条数
+        if (!is_numeric($pageNum) || !is_numeric($page)) {
+            $this->fail(500, 'page或pageNum参数校验错误');
+        }
+        $data = Db::table('tb_user_game')
+            ->alias('ug')
+            ->join('tb_game g', 'ug.game_id = g.id')
+            ->where(['ug.user_id' => $this->getUser()->user_id])
+            ->field('ug.*,g.title as game_title,g.image_url as game_image_url,g.description as game_description')
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $pageNum,
+            ]);
+        $this->success('success', $data);
+    }
+    
+    //电商渠道购买,兑换购买的游戏
+    public function exchangeCdKeyGame()
+    {
+        $cdKey = $this->request->post('cdKey');
+        if (empty($cdKey)) {
+            $this->fail(500, 'cdKey参数校验错误');
+        }
+        $data = Db::table('tb_cdkey')
+            ->where(['cdkey' => $cdKey])
+            ->find();
+        if (!$data) {
+            $this->fail(501, 'cdKey不存在');
+        }
+        if ($data['state'] == 1) {
+            $this->fail(502, 'cdKey已经使用过');
+        }
+        //如果过期了做一下更新
+        if ($data['expired_at'] < time()) {
+            Db::table('tb_cdkey')
+                ->where(['id' => $data['id']])
+                ->update(['state' => 2]);
+            $data['state'] = 2;
+        }
+        if ($data['state'] == 2) {
+            $this->fail(503, 'cdKey已经过期');
+        }
+        //使用cdKey
+        Db::table('tb_cdkey')
+            ->where(['id' => $data['id']])
+            ->update([
+                'state'  => 1,
+                'use_at' => time(),
+            ]);
+        //兑换购买渠道选择
+        $channel = 2;
+        if (str_starts_with($data['cdkey'], 'tb')) {
+            $channel = 2;
+        } elseif (str_starts_with($data['cdkey'], 'pdd')) {
+            $channel = 3;
+        }
+        //获取游戏的价格
+        $game = Db::table('tb_game')
+            ->where(['id' => $data['game_id']])
+            ->find();
+        $price = $game['price'];
+        //购买游戏
+        Db::table('tb_user_game')
+            ->insert([
+                'user_id'    => $this->getUser()->user_id,
+                'game_id'    => $data['game_id'],
+                'price'      => $price,
+                'channel'    => $channel,
+                'created_at' => time(),
+            ]);
+        $this->success('success', [
+            'gameId' => $data['game_id'],
+        ]);
+    }
+    
+    //获取我使用过的兑换码
+    public function getMyUsedCdKey()
+    {
+        $pageNum = $this->request->post('pageNum');   //每页显示的数据条数
+        if (!is_numeric($pageNum)) {
+            $this->fail(500, 'pageNum参数校验错误');
+        }
+        $data = Db::table('tb_cdkey')
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->where(['state'   => 1])
+            ->paginate($pageNum);
+        $this->success('success', $data);
+    }
+    
+    //搜索游戏
+    public function searchGame()
+    {
+        $keywords = $this->request->post('keywords'); //关键字
+        if (empty($keywords)) {
+            $this->fail(500, '关键字不能为空');
+        }
+        if (strlen($keywords) >= 100) {
+            $this->fail(501, '关键字长度超限');
+        }
+        $data = Db::table('tb_game')
+            ->where('title', 'like', '%' . $keywords . '%')
+            ->select();
+        Db::table('tb_search_keywords')
+            ->insert([
+                'keywords'   => $keywords,
+                'created_at' => time(),
+            ]);
+        $this->success('success', [
+            'count' => count($data),
+            'data'  => $data,
+        ]);
+    }
+    
+    //获取热搜关键字
+    public function getHotSearchKeywords()
+    {
+        $data = Db::table('tb_search_keywords')
+            ->order('created_at DESC')
+            ->limit(5)
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    // 根据CDKey获取游戏下载地址以及提取码
+    public function getGameDownloadForCDkey()
+    {
+        $cdkey = $this->request->post('cdkey');
+        if (!$cdkey) {
+            $this->fail('请填写正确的cdkey!', 500);
+        }
+        // 校验CDKey是否存在以及CDkey状态
+        $row = Db::table('tb_cdkey')->where('cdkey', $cdkey)->find();
+        if (!$row) {
+            $this->fail('cdkey不存在!', 500);
+        }
+        // if ($row['state'] != 0) {
+        //     $this->fail('cdkey已使用或已过期!', 500);
+        // }
+        // 校验通过 返回对应游戏下载地址与提取码以及更新CDkey状态
+        // 查询对应游戏信息
+        $game = Db::table('tb_game')->where('id', $row['game_id'])->find();
+        if (!$game) {
+            $this->fail('对应游戏不存在!请与管理员联系!', 500);
+        }
+        // Db::table('tb_cdkey')->where('cdkey', $cdkey)->update(['state' => 1]);
+        $data = ['download_url' => $game['download_url'], 'extracted_code' => $game['extracted_code']];
+        $this->success('success', $data);
+    }
+    
+    //推荐游戏(最多只显示5个)
+    public function recommendedGames()
+    {
+        $gameId = $this->request->post('gameId');
+        if (!is_numeric($gameId)) {
+            $this->fail(500, 'gameId参数校验错误');
+        }
+        //优先推荐优先级靠前的同类型游戏
+        $game = Db::table('tb_game')
+            ->where(['id' => $gameId])
+            ->find();
+        if (!$game) {
+            $this->fail(501, '找不到对应的gameId');
+        }
+        // $categoryId = $game['id'];
+        $categoryId = $game['category'];
+        //只返回推荐关键信息,不要返回下载链接,提取码等重要信息
+        $data = Db::table('tb_game')
+            ->field('id,category,title,image_url,price,created_at,updated_at')
+            ->where(['category' => $categoryId])
+            ->order('priority ASC')
+            ->limit(5)
+            ->select();
+        $this->success('success', $data);
+    }
+
+    // 获取指定用户的库存游戏
+    public function getGameByUserId()
+    {
+        $page = $this->request->post('page');   // 当前页数
+        $list = $this->request->post('list');   // 每页记录数
+        $user_id = $this->request->post('user_id'); // 用户ID
+        if (!is_numeric($user_id)){
+            $this->fail(500, '用户ID未找到');
+        }
+        // 用户验证
+        $user = Db::table('tb_user')->where('id', $user_id)->find();
+        if (!$user) {
+            $this->fail(500, '用户不存在!');
+        }
+        // 查找用户绑定的游戏
+        $data = Db::table('tb_game_bind_record')
+            ->alias('tgbr')
+            ->where('tgbr.user_id', $user_id)
+            ->join('tb_game tg', 'tgbr.game_id = tg.id')
+            ->field('tgbr.*, tg.title, tg.image_url, tg.game_version, tg.browse, tg.language')
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $list,
+            ]);
+        // foreach($data->getCollection()->toArray() as &$item) {
+        //     exit($item['avatar']);
+        //     $item['avatar'] = $this->getUserAvatar($item['avatar']);
+        // }
+        $this->success('success', $data);
+    }
+}

+ 20 - 0
app/controller/Kefu.php
View File

@@ -0,0 +1,20 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//客服控制器
+class Kefu extends BaseController
+{
+    protected $noNeedLogin = ['getQQKf'];
+    
+    //获取QQ客服链接
+    public function getQQKf()
+    {
+        $data = Db::table('tb_kefu')
+            ->where(['type' => 1])  //QQ客服
+            ->find();
+        $this->success('success', $data);
+    }
+}

+ 106 - 0
app/controller/Message.php
View File

@@ -0,0 +1,106 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//消息控制器
+class Message extends BaseController
+{
+    //发送消息
+    public function sendMessage()
+    {
+        $toUid = $this->request->post('toUid');  //发送给用户ID
+        $msg = $this->request->post('msg');      //发送的消息内容
+        if (!is_numeric($toUid) || empty($msg)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $messageId = Db::table('tb_message')
+            ->insertGetId([
+                'from_uid'   => $this->getUser()->user_id,
+                'to_uid'     => $toUid,
+                'msg'        => $msg,
+                'is_read'    => 0,
+                'created_at' => time(),
+            ]);
+        $this->success('success', [
+            'messageId' => $messageId,
+        ]);
+    }
+    
+    //读消息
+    public function readMessage()
+    {
+        $messageId = $this->request->post('messageId');   //消息ID
+        if (!is_numeric($messageId)) {
+            $this->fail(500, 'messageId参数校验错误');
+        }
+        $message = Db::table('tb_message')
+            ->where(['id' => $messageId])
+            ->find();
+        if (!$message) {
+            $this->fail(501, 'messageId不存在');
+        }
+        if ($message['to_uid'] != $this->getUser()->user_id) {
+            $this->fail(502, '没有权限读消息');
+        }
+        $message['is_read'] = 1;
+        $message['read_at'] = time();
+        Db::table('tb_message')
+            ->where(['id' => $messageId])
+            ->update([
+                'is_read' => 1,
+                'read_at' => time(),
+            ]);
+        $this->success('success', $message);
+    }
+    
+    //获取我的所有消息
+    public function getMyAllMessages()
+    {
+        $page = $this->request->post('page');         //页码
+        $pageNum = $this->request->post('pageNum');         //每页显示的数据条数
+        if (!is_numeric($pageNum) || !is_numeric($page)) {
+            $this->fail(500, 'page或pageNum参数校验错误');
+        }
+        $data = Db::table('tb_message')
+            ->where(['to_uid' => $this->getUser()->user_id])
+            ->order('created_at DESC')
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $pageNum,
+            ]);
+        
+        // 使用 each 方法处理分页数据
+        $data->each(function($item) {
+            $fromUser = Db::table('tb_user')
+                ->where(['id' => $item['from_uid']])
+                ->field('username,avatar')
+                ->find();
+            
+            if ($fromUser) {
+                // 处理系统头像
+                if (str_starts_with($fromUser['avatar'], 'system://')) {
+                    $avatarData = explode('//', $fromUser['avatar']);
+                    $avatarId = intval($avatarData[1]);
+                    $avatar = Db::table('tb_system_avatar')
+                        ->where(['id' => $avatarId])
+                        ->field('image_url')
+                        ->find();
+                    if ($avatar) {
+                        $fromUser['avatar'] = $avatar['image_url'];
+                    }
+                }
+                
+                // 只保留需要的字段
+                $item['from_user'] = [
+                    'username' => $fromUser['username'],
+                    'avatar' => $fromUser['avatar']
+                ];
+            }
+            
+            return $item;
+        });
+        $this->success('success', $data);
+    }
+}

+ 246 - 0
app/controller/Pay.php
View File

@@ -0,0 +1,246 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+
+//支付控制器
+class Pay extends BaseController
+{
+    protected $noNeedLogin = ['payOrder','payCallback'];
+    public $baseUrl = 'https://pay.v8jisu.cn';
+    public $merchantId = '28399';
+    public $key = '0QknLivpRQB50Bq76r06PiR464SrTrvQ';
+    public $merchantPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy9M3hkPM/rAeow0Y6bjJzHFy4btLxIZ4gMB3ggqP1MBDTg+oLwrVfU8OUxYG7/yntiFqDFNOAopccygsNRTzdrWQjenbJ4g9xCwWiIiubMRAKfgYNX9AsUalxcXpWPvmCLKHc4YdHGh8+GrV0qZi/FAQ7uMLXeowe+Np9cUziXIDLYbgJXB0UBqEyI2GVBJMOuFqKaP5pay/DcTTQwLKcHyzPlOUTlMet4ClPrAhqfe/FQYIfa8nFtrSevAzPMwJuZfYrFr6DvyQADaP16RZQS0tLMPW47646ieQ7GmITixXd5yEzlBTlECh1sIKyzn9DjzLkmrVFsDim4NFgJp/eQIDAQAB';
+    public $merchantPrivateKey = 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkSKP7fp5hjHFgYufalba3+qeJMPKHhXCm1yk7jJKkOIHkgW8tpcQqw/c8krwIRdjW6W5YF5Jr/b3cXP1JmyIeyLfbglSck+GijdFGMWlwbNAPVO/TrJUy8/+beFrqVJdCBlsRYW6lml4aGMXFTIK3Rn69zRfvgUFDYcZOa0VGTfXjfR0hOME6vxw1DxCOvWxczh8uLSeFnbirIyKaES807XGC3eCTmyJi/UsPA3vQeUUTpXliitSZFH/+T6g2K4n0Oy2OER5cTFWL8p/Gpy/pxoxALrc9eSIv8diLIco7WPfFYPABeZFe0cF4FwBeVaCqh1/K9m9uE6SAXhJUKZbzAgMBAAECggEAahuRjQ5Xk8Px1vliB2nbWjy5rrz/nhpaOFJ+Kd23M3nIdmvrP25zdeVMf+08VSQSHCK4VV3vgx6YJ1tZp+LhwylMvE0iAv2BvUrp4RSKi+Un+FhkeSEY4GwlfSA+MflLrTbDEZsWEQdlgf/NvV1IzOOJebNg0sRjj2xc/opB0uD9Dp2CMmdR4cztUXlmnVx0i5UQxl59u0TQitcuriTmqre4qibYX3ddr1V91k7WCde25a7HEdoQSID566O3H3tE6+XSIEp6LXpYrZFSK+b9Zc+V7khr27AmEMsSPpy/nSPI8pKwjaoFbLaGrh9PToXin5ENGbVY79fARIVe+HfAoQKBgQD1K1yqSEs+F09Aml6xY5BjqJ14HJQ8DphktrGzlI0hYHi42OtEfaPOrThLHGes4TipJUnvWjG0YqNaW+t9j2733OShUNF5BqiwTyfZ1/F+UffU+m6Dq0aB/DZV7KGB6rfQ1XUn77rIMs0gcm7BMwhfZLXUWDhj6qen+kMCO9PY+QKBgQDuXlFaQh1/ByR/urOqU6x9EGr5B0iT2QXM1vKbGOpj2wZIRV+wJtfnSy5kTxRigGgm1gXHgqYwRrONGR/0O/yfNVEm/OuEy4IUNBXsU/vjmzpfSNt2ajY91bMuf6aAZ1M688o08ouL4mrCKWQHUXIN8jxnPN6Yp84TcotghZi2SwKBgQCO6TnY4M9LYFcIN3PfP0RZc15nN3GJGJDolD49iehCfnOgfIGXqQ0lWn+n+OTON3LJ1jyk0xSKK71A3LgGtudegFqdVfjk7WbDb0CxkVjp42ntshVdlydAef5KU+dJTcLcbrEeGHXuYP6FXW8GG3NT9+at4sbsJ0qXdiA9WxaAMQKBgHDV5u6x42KRT/7Cs2/KYhllny24++s4zV0U1w0CM1oHgSbO6Cfri0JqvVAwevbRz/uqTlwOBXtOzInbPdwQVVpME9k/2oEnELFdoo8XhmJMxcn7JCAe0QReV46IUJnxz11Vr/92XQZfrKeyji5EqJffdiZskvZyYMOl8kJDm3GXAoGBAOOd8/HCGGeaMA7Uv9bjW17BQ9d/mJ/KmBI/S0t0/Y5Ur7hdtcS/5fw9lHPIqXTP+CJhE5+sQOi5GiwWHy0CCUlWFfi3kwlNTrau3m56sx7chjvaY/1F3b4sG7eFJMbgi8BjXiZREHJ1dehczF9pguIT/rcCtkpDMaWy8DIrSRdp';
+
+    // 生成RSA公钥
+    private function makeRsaSign($data)
+    {
+        // 排序 & 拼接
+        ksort($data);
+        $str = '';
+        foreach ($data as $k => $v) {
+            if ($v !== "" && $k != 'sign' && $k != 'sign_type' && !is_array($v)) {
+                $str .= "{$k}={$v}&";
+            }
+        }
+        $str = rtrim($str, "&");
+
+        // 拼接PEM格式
+        $privateKey = "-----BEGIN PRIVATE KEY-----\n"
+            . chunk_split($this->merchantPrivateKey, 64, "\n")
+            . "-----END PRIVATE KEY-----\n";
+
+        $res = openssl_get_privatekey($privateKey);
+        if (!$res) {
+            throw new \Exception('私钥格式错误或无法加载');
+        }
+        openssl_sign($str, $sign, $res, OPENSSL_ALGO_SHA256);
+        return base64_encode($sign);
+    }
+
+    // 生成 MD5 签名
+    private function makeMd5Sign($data)
+    {
+        $md5_key = $this->key; // 商户密钥
+        ksort($data); // 按照 ASCII 码排序
+        $str = '';
+        foreach ($data as $k => $v) {
+            if ($v !== "" && $k != 'sign' && $k != 'sign_type') { 
+                // 排除空值、sign 和 sign_type
+                $str .= "{$k}={$v}&";
+            }
+        }
+        $str = rtrim($str, "&"); // 去掉最后一个 &
+        $str .= $md5_key; // 按文档要求,直接拼接 KEY
+    
+        return md5($str); // 小写
+    }
+    
+    // 发起Http请求
+    private function HttpRequest($url, $postData)
+    {
+        // 将数组编码为 x-www-form-urlencoded 格式的查询字符串
+        $postData1 = http_build_query($postData);
+        // 初始化 cURL 会话
+        $ch = curl_init($url);
+        // 设置 cURL 选项
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  // 将响应作为字符串返回
+        curl_setopt($ch, CURLOPT_POST, true);            // 使用 POST 方法
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData1); // 设置 POST 数据
+        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
+            'Content-Type: application/x-www-form-urlencoded' // 设置 Content-Type 头
+        ));
+        // 执行请求并获取响应
+        $response = curl_exec($ch);
+        // 关闭 cURL 会话
+        curl_close($ch);
+        // 检查请求是否成功
+        if ($response === false) {
+            return "cURL Error: " . curl_error($ch);
+        } else {
+            // 处理响应
+            return $response;
+        }
+    }
+
+    // JSON清理
+    function fixEscapedJson($jsonString) {
+        // 移除多余的反斜杠
+        $fixed = stripslashes($jsonString);
+        // 替换HTML实体
+        $fixed = str_replace(
+            ['&quot;', '&amp;', '&lt;', '&gt;'],
+            ['"', '&', '<', '>'],
+            $fixed
+        );
+        return $fixed;
+    }
+    
+    //拉起订单
+    public function order()
+    {
+        $goodsName = $this->request->post('goodsName');     // 商品名称
+        $amount = $this->request->post('amount');           // 商品金额
+        $type = $this->request->post('type');               // 支付方式
+        $isVip = $this->request->post('isVip') ?? 0;        // 是否为购买VIP
+        $user = $this->getUser();
+        if (!$goodsName || !$type || !is_numeric($amount)) {
+            $this->fail(500, '参数校验错误');
+        }
+        if (!$user) {
+            $this->fail(500, '用户未登录');
+        }
+
+        // 参数构建
+        $data['pid'] = $this->merchantId;
+        $data['method'] = 'jump';    // 接口类型
+        $data['device'] = 'pc'; // 设备类型
+        $data['type'] = $type; // 支付方式
+        $data['out_trade_no'] = date('YmdHis') . strval(mt_rand(100000, 999999)); // 订单号
+        $data['notify_url'] = 'https://api.danjiwanjia.com/pay/payCallback'; // 服务器异步通知地址
+        $data['return_url'] = 'https://www.danjiwanjia.com/#/userCenter'; // 页面跳转通知地址
+        $data['name'] = $goodsName; // 商品名称	
+        $data['money'] = $amount; // 商品金额	
+        $data['clientip'] = $this->request->ip(); // 用户ip地址
+        $data['timestamp'] = time(); // 当前时间戳
+        $data['param'] = json_encode(['isVip' => $isVip]);   // 业务拓展参数 通过此参数判断用户是否通过VIP页面调起支付
+        $data['sign_type'] = 'RSA'; // 当前时间戳
+        $data['sign'] = $this->makeRsaSign($data);  // 签名生成
+
+        // 插入数据表
+        Db::table('tb_user_order')
+            ->insert([
+                'user_id'    => $user->user_id,
+                'balance'    => $amount,
+                'state'      => 0,
+                'created_at' => time(),
+                'order_no'   => $data['out_trade_no'],
+            ]);
+
+        // 发起请求
+        $url = $this->baseUrl . '/api/pay/create';
+        $result = $this->HttpRequest($url, $data);
+        // header('Content-Type: application/json');
+        // echo $result;
+        // exit();
+        $result = json_decode($result, true);
+        if($result['code'] == 0) {
+            $this->success('success', $result['pay_info']);
+        } else {
+            $this->fail(500, $result['msg']);
+        }
+    }
+    
+    //获取我的订单
+    public function getMyOrder()
+    {
+        $page = $this->request->post('page');         //页码
+        $pageNum = $this->request->post('pageNum');         //每页显示的数据条数
+        if (!is_numeric($pageNum) || !is_numeric($page)) {
+            $this->fail(500, 'page或pageNum参数校验错误');
+        }
+        $data = Db::table('tb_user_order')
+            ->where(['user_id' => $this->getUser()->user_id])
+            ->order('created_at DESC')
+            ->paginate([
+                'page' => $page,
+                'list_rows' => $pageNum,
+            ]);
+        $this->success('success', $data);
+    }
+    
+    /**
+     * 支付回调
+     */
+    public function payCallback()
+    {
+        $data = $this->request->param();
+        error_log('payCallback: '.json_encode($data));
+        if(!$data) {
+            exit('无法获取到传参');
+        }
+        $order_sn = $data['out_trade_no'];
+        $amount = $data['money'];
+        $isVip = json_decode($this->fixEscapedJson($data['param']), true)['isVip'];
+        $order = Db::name('tb_user_order')->where('order_no', $order_sn)->find();
+        if (!$order) {
+            exit('订单不存在');
+        }
+        if ($order['state'] == 1) {
+            // 订单已支付 不走后面的逻辑但是要返回success
+            exit('success');
+        }
+        // 订单校验通过 更新订单状态
+        Db::name('tb_user_order')
+        ->where('order_no', $order_sn)
+        ->update([
+            'state' => 1,
+            'pay_at' => time(),
+        ]);
+        // 业务判断
+        if($isVip == 1) {
+            // 增加用户VIP时长
+            // 查找对应价格VIP等级
+            $vipInfo = Db::name('tb_user_vip_price')
+                ->where('user_id', $order['user_id'])
+                ->where('price', $amount)
+                ->find();
+            // 首先判断当前用户是否已经为VIP
+            $userVip = Db::name('tb_user_vip')->where('user_id', $order['user_id'])->find();
+            if($userVip) {
+                // 如果用户已经为VIP 且为同类型VIP则增加VIP时长
+                if($userVip['type'] == $vipInfo['type']) {
+                    Db::name('tb_user_vip')
+                        ->where('user_id', $order['user_id'])
+                        ->setInc('expired_at', strtotime($vipInfo['duration'] . 'month'));
+                } else {
+                    // 如果用户已经为VIP 且为不同类型VIP则删除原有VIP记录并新增VIP记录
+                    Db::name('tb_user_vip')
+                        ->where('user_id', $order['user_id'])
+                        ->delete();
+                    Db::name('tb_user_vip')->insert([
+                        'user_id' => $order['user_id'],
+                        'type' => $vipInfo['type'],
+                        'expired_at' => strtotime($vipInfo['duration'] . 'month')
+                    ]);
+                }
+            } else {
+                // 如果用户不是VIP 则新增VIP记录
+                Db::name('tb_user_vip')->insert([
+                    'user_id' => $order['user_id'],
+                    'type' => $vipInfo['type'],
+                    'expired_at' => strtotime($vipInfo['duration'] . 'month')
+                ]);
+            }
+        } else {
+            // 增加用户余额
+            Db::name('tb_user')
+                ->where('id', $order['user_id'])
+                ->inc('balance', $amount * 2)   // 限时活动 实际到账金额为金额的2倍
+                ->update();
+        }
+        exit('success');
+    }
+}

+ 141 - 0
app/controller/Payment.php
View File

@@ -0,0 +1,141 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+use phpu\facade\ThinkCaptcha;
+use Ramsey\Uuid\Uuid;
+use think\captcha\facade\Captcha;
+
+/**
+ * 支付类(彩虹易支付)
+ */
+
+class Payment extends BaseController
+{
+    protected $noNeedLogin = ['payOrder','payCallback'];
+    public $baseUrl = 'https://pay.v8jisu.cn';
+    public $merchantId = '28399';
+    public $key = '0QknLivpRQB50Bq76r06PiR464SrTrvQ';
+
+    // 生成 MD5 签名
+    private function makeMd5Sign($data)
+    {
+        $md5_key = $this->key; // 商户密钥
+        ksort($data); // 按照 ASCII 码排序
+        $str = '';
+        foreach ($data as $k => $v) {
+            if ($v !== "" && $k != 'sign' && $k != 'sign_type') { 
+                // 排除空值、sign 和 sign_type
+                $str .= "{$k}={$v}&";
+            }
+        }
+        $str = rtrim($str, "&"); // 去掉最后一个 &
+        $str .= $md5_key; // 按文档要求,直接拼接 KEY
+    
+        return md5($str); // 小写
+    }
+
+    //请求
+    private function HttpRequest($url, $postData)
+    {
+        // 将数组编码为 x-www-form-urlencoded 格式的查询字符串
+        $postData1 = http_build_query($postData);
+        // 初始化 cURL 会话
+        $ch = curl_init($url);
+        // 设置 cURL 选项
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  // 将响应作为字符串返回
+        curl_setopt($ch, CURLOPT_POST, true);            // 使用 POST 方法
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData1); // 设置 POST 数据
+        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
+            'Content-Type: application/x-www-form-urlencoded' // 设置 Content-Type 头
+        ));
+        // 执行请求并获取响应
+        $response = curl_exec($ch);
+        // 关闭 cURL 会话
+        curl_close($ch);
+        // 检查请求是否成功
+        if ($response === false) {
+            return "cURL Error: " . curl_error($ch);
+        } else {
+            // 处理响应
+            return $response;
+        }
+    }
+
+    /**
+     * 发起支付
+     */
+    public function payOrder()
+    {
+        $goodsName = $this->request->post('goodsName');     // 商品名称
+        $goodsPrice = $this->request->post('goodsPrice');   // 商品金额
+        $type = $this->request->post('type');               // 支付方式
+        $user_id = $this->getUser()->user_id;               // 用户ID
+        if(!$user_id) {
+            $this->fail(500, '用户未登录');
+        }
+        $data['pid'] = $this->merchantId;
+        // $data['method'] = 'web';    // 接口类型
+        // $datap['device'] = 'pc'; // 设备类型
+        $data['type'] = $type; // 支付方式
+        $data['out_trade_no'] = date("YmdHis") . rand(100, 999); // 订单号
+        $data['notify_url'] = 'https://api.danjiwanjia.com/payment/payCallback'; // 服务器异步通知地址
+        $data['return_url'] = 'https://www.danjiwanjia.com/#/userCenter'; // 页面跳转通知地址
+        $data['name'] = $goodsName; // 商品名称	
+        $data['money'] = $goodsPrice; // 商品金额	
+        $data['clientip'] = $this->request->ip(); // 用户ip地址
+        $data['timestamp'] = time(); // 当前时间戳
+        $data['sign_type'] = 'MD5'; // 当前时间戳
+        $data['sign'] = $this->makeMd5Sign($data);  // 签名生成
+
+        // 插入数据库
+        Db::name('tb_user_order')->insert([
+            'user_id' => $user_id,
+            'order_no' => $data['out_trade_no'],
+            'balance' => $goodsPrice,
+            'state' => 0,
+            'create_at' => time(),
+        ]);
+
+        $url = $this->baseUrl . '/mapi.php';
+        $result = $this->HttpRequest($url, $data);
+        // header('Content-Type: application/json');
+        // echo $result;
+        // exit();
+        if($result['code'] == 1) {
+            $this->success('success', $result['payurl']);
+        } else {
+            $this->fail(500, $result['msg']);
+        }
+    }
+
+    /**
+     * 支付回调
+     */
+    public function payCallback()
+    {
+        $data = $this->request->param();
+        if(!$data) {
+            exit('success');
+        }
+        $order_sn = $data['out_trade_no'];
+        $amount = $data['money'];
+        $order = Db::name('tb_user_order')->where('order_no', $order_sn)->find();
+        if (!$order) {
+            exit('订单不存在');
+        }
+        if ($order['state'] == 1) {
+            // 订单已支付 不走后面的逻辑但是要返回success
+            exit('success');
+        }
+        // 订单校验通过 更新订单状态
+        Db::name('tb_user_order')
+        ->where('order_no', $order_sn)
+        ->update([
+            'state' => 1,
+            'pay_at' => time(),
+        ]);
+        exit('success');
+    }
+}

+ 304 - 0
app/controller/User.php
View File

@@ -0,0 +1,304 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+use phpu\facade\ThinkCaptcha;
+use Ramsey\Uuid\Uuid;
+use think\captcha\facade\Captcha;
+
+class User extends BaseController
+{
+    protected $noNeedLogin = ['login', 'loginV2', 'logout', 'register', 'getSystemAvatar', 'getRegisterCaptchaImage', 'getLoginCaptchaImage', 'checkUsernameExists'];
+    
+    //登录
+    public function login()
+    {
+        $username = $this->request->post('username');   //用户名
+        $password = $this->request->post('password');   //密码
+        //$captcha = $this->request->post('captcha');     //图形验证码,如果输错了需要重新刷新验证码图片
+        if (empty($username) || empty($password)) {
+            $this->fail(500, '参数校验错误');
+        }
+       // if (!ThinkCaptcha::check($captcha, 'login', 0)) {
+       //     $this->fail(501, '验证码错误');
+       // }
+        $data = Db::table('tb_user')
+            ->where(['username' => $username])
+            ->where(['password' => md5($password)])
+            ->find();
+        if (!$data) {
+            $this->fail(502, '用户名或密码错误');
+        }
+        //检测是否激活
+        if ($data['state'] == 0) {
+            $this->fail(503, '账号未激活,请前往您的邮箱地址:' . $data['email'] . ',进行验证通过~');
+        }
+        //检测是否被封号
+        if ($data['state'] == 2) {
+            $this->fail(504, '您的账号已被封停~');
+        }
+        //更新登录信息
+        Db::table('tb_user')
+            ->where(['id' => $data['id']])
+            ->update([
+                'login_count'   => $data['login_count'] + 1,
+                'last_login_ip' => $this->request->ip(),
+                'last_login'    => time(),
+            ]);
+        //写登录日志
+        Db::table('tb_user_login_log')
+            ->insert([
+                'user_id'    => $data['id'],
+                'ip'         => $this->request->ip(),
+                'user_agent' => $this->request->header('user-agent'),
+                'created_at' => time(),
+            ]);
+        //写登录Token
+        $token = Uuid::uuid4()->toString();
+        Db::table('tb_user_token')
+            ->insert([
+                'user_id'    => $data['id'],
+                'token'      => $token,
+                'expired_at' => time() + 24 * 60 * 60,
+            ]);
+        $this->success('success', [
+            'token' => $token
+        ]);
+    }
+    
+    
+    //登录
+    public function loginV2()
+    {
+        $username = $this->request->post('username');   //用户名
+        $password = $this->request->post('password');   //密码
+        //$captcha = $this->request->post('captcha');     //图形验证码,如果输错了需要重新刷新验证码图片
+        if (empty($username) || empty($password)) {
+            $this->fail(500, '参数校验错误');
+        }
+       // if (!ThinkCaptcha::check($captcha, 'login', 0)) {
+       //     $this->fail(501, '验证码错误');
+       // }
+        $data = Db::table('tb_user')
+            ->where(['username' => $username])
+            ->where(['password' => md5($password)])
+            ->find();
+        if (!$data) {
+            $this->fail(502, '用户名或密码错误');
+        }
+        //检测是否激活
+        if ($data['state'] == 0) {
+            $this->fail(503, '账号未激活,请前往您的邮箱地址:' . $data['email'] . ',进行验证通过~');
+        }
+        //检测是否被封号
+        if ($data['state'] == 2) {
+            $this->fail(504, '您的账号已被封停~');
+        }
+        //更新登录信息
+        Db::table('tb_user')
+            ->where(['id' => $data['id']])
+            ->update([
+                'login_count'   => $data['login_count'] + 1,
+                'last_login_ip' => $this->request->ip(),
+                'last_login'    => time(),
+            ]);
+        //写登录日志
+        Db::table('tb_user_login_log')
+            ->insert([
+                'user_id'    => $data['id'],
+                'ip'         => $this->request->ip(),
+                'user_agent' => $this->request->header('user-agent'),
+                'created_at' => time(),
+            ]);
+        //写登录Token
+        $token = Uuid::uuid4()->toString();
+        Db::table('tb_user_token')
+            ->insert([
+                'user_id'    => $data['id'],
+                'token'      => $token,
+                'expired_at' => time() + 24 * 60 * 60,
+            ]);
+        $this->success('success', [
+            'token' => $token
+        ]);
+    }
+    
+    //退出登录
+    public function logout()
+    {
+        $token = $this->request->post('token');
+        if (empty($token)) {
+            $this->fail(500, 'token参数校验错误');
+        }
+        Db::table('tb_user_token')
+            ->where(['token' => $token])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //修改密码,修改成功后token会释放,需要转跳到登录页重新登录
+    public function changePwd()
+    {
+        $oldPassword = $this->request->post('oldPassword');
+        $newPassword = $this->request->post('newPassword');
+        if (empty($oldPassword) || empty($newPassword)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = $this->getUser();
+        $data = Db::table('tb_user')
+            ->where(['id'       => $user->user_id])
+            ->where(['password' => md5($oldPassword)])
+            ->find();
+        if (!$data) {
+            $this->fail(501, '当前密码错误');
+        }
+        Db::table('tb_user')
+            ->where(['id' => $user->user_id])
+            ->update([
+                'password' => md5($newPassword),
+            ]);
+        //释放token,Token会立马失效
+        Db::table('tb_user_token')
+            ->where(['token' => $user->token])
+            ->delete();
+        $this->success('success', null);
+    }
+    
+    //获取系统自带头像
+    public function getSystemAvatar()
+    {
+        $data = Db::table('tb_system_avatar')
+            ->order('id ASC')
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    //获取注册验证码图片
+    public function getRegisterCaptchaImage()
+    {
+        return ThinkCaptcha::printImg('register');
+        //return Captcha::create('register');
+    }
+    
+    
+    //获取登录验证码图片
+    public function getLoginCaptchaImage()
+    {
+        return ThinkCaptcha::printImg('login');
+    }
+    
+    //检查用户名是否存在(被注册)
+    public function checkUsernameExists()
+    {
+        $username = $this->request->post('username');
+        if (empty($username)) {
+            $this->fail('用户名不能为空');
+        }
+        $exists = true;
+        $user = Db::table('tb_user')
+            ->where(['username' => $username])
+            ->find();
+        if (!$user) {
+            $exists = false;
+        }
+        $this->success('success', [
+            'isUsernameExists' => $exists,
+        ]);
+    }
+    
+    //注册
+    public function register()
+    {
+        $username = $this->request->post('username');   //用户名
+        $password = $this->request->post('password');   //密码
+        $avatar = $this->request->post('avatar');       //头像
+        $email = $this->request->post('email');         //邮箱账号
+        $captcha = $this->request->post('captcha');     //图形验证码,如果输错了需要重新刷新验证码图片
+        
+        if (empty($username) || empty($password) || !is_numeric($avatar) || empty($email)) {
+            $this->fail(500, '参数校验错误');
+        }
+        // var_dump(Captcha::check($captcha, 'register'));
+        // echo ThinkCaptcha::check($captcha, 'register', 0);
+        // exit();
+        // if (!ThinkCaptcha::check($captcha, 'register')) {
+        //     $this->fail(501, '验证码错误');
+        // }
+        $user = Db::table('tb_user')
+            ->where(['username' => $username])
+            ->find();
+        if ($user) {
+            $this->fail(502, '用户名已经被注册');
+        }
+        //拦截邮箱是否被注册
+        $emailUser = Db::table('tb_user')
+            ->where(['email' => $email])
+            ->find();
+        if ($emailUser) {
+            $this->fail(503, '邮箱账号已经被使用过了~');
+        }
+        Db::table('tb_user')
+            ->insert([
+                'username'          => $username,     //用户名
+                'password'          => md5($password),  //密码
+                'created_at'        => time(),  //创建时间
+                'last_login'        => time(),  //上次登录时间
+                'login_count'       => 0,   //登录次数
+                'avatar'            => 'system://' . $avatar,   //头像URL
+                'state'             => 1,  //状态 0:未激活   1:正常   2:封号
+                'balance'           => 0,  //余额
+                'email'             => $email,  //邮箱账号
+                'is_email_verified' => 0,  //邮箱是否认证  0:未认证  1:已认证
+                'score'             => 0,  //积分数量
+                'gold'              => 0,  //金币数量
+                'exp'               => 0,  //经验值
+            ]);
+        $this->success('success', null);
+    }
+    
+    //取用户信息
+    public function getUserInfo()
+    {
+        $user = Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->find();
+        //解析获取头像url
+        if (str_starts_with($user['avatar'], 'system://')) {
+            $data = explode('//', $user['avatar']);
+            $avatarId = intval($data[1]);
+            $avatar = Db::table('tb_system_avatar')
+                ->where(['id' => $avatarId])
+                ->find();
+            $user['avatar'] = $avatar['image_url'];
+        }
+        unset($user['password']);
+        $this->success('success', $user);
+    }
+    
+    //上传头像
+    public function uploadAvatar()
+    {
+        $file = $this->request->file('file');
+        if (empty($file)) {
+            $this->fail(500, '上传的图片文件不能为空');
+        }
+        // 使用验证器验证上传的文件
+        validate(['file' => [
+            // 限制文件大小(单位b),这里限制为2M
+            'fileSize' => 2 * 1024 * 1024,
+            // 限制文件后缀,多个后缀以英文逗号分割
+            'fileExt'  => 'jpg,jpeg,png',
+        ]])->check(['file' => $file]);
+        $saveName = \think\facade\Filesystem::disk('public')->putFile('avatar', $file);
+        Db::table('tb_user')
+            ->where(['id' => $this->getUser()->user_id])
+            ->update([
+                'avatar' => $this->request->scheme() . '://' . $this->request->host() . '/storage/' . $saveName,
+            ]);
+        $this->success('success', [
+            'filePath' => $this->request->scheme() . '://' . $this->request->host() . '/storage/' . $saveName,
+        ]);
+    }
+    
+}

+ 210 - 0
app/controller/Vip.php
View File

@@ -0,0 +1,210 @@
+<?php
+namespace app\controller;
+
+use app\BaseController;
+use think\facade\Db;
+use phpu\facade\ThinkCaptcha;
+use Ramsey\Uuid\Uuid;
+use think\captcha\facade\Captcha;
+
+//VIP控制器
+class Vip extends BaseController
+{
+    protected $noNeedLogin = [];
+    private $platinumVip = [20, 54, 96, 120];  //铂金VIP价格
+    private $diamondVip = [30, 81, 144, 180];  //钻石VIP价格
+    
+    //获取用户VIP信息,一般用于前端显示
+    public function getUserVip()
+    {
+        $user = $this->getUser();
+        $vip = Db::table('tb_user_vip')
+            ->where(['user_id' => $user->user_id])
+            ->find();
+        if (!$vip) {
+            $this->fail(500, '当前用户不是VIP');
+        }
+        if ($vip['expired_at'] < time()) {
+            $this->fail(501, 'VIP已到期');
+        }
+        $this->success('success', $vip);
+    }
+    
+    //获取VIP价格
+    //铂金VIP,包月20R,包季度54R,包半年96R,包年120R
+    //钻石VIP,包月30R,包季度81R,包半年144R,包年180R
+    public function getVipPrice()
+    {
+        $type = $this->request->post('type');    //VIP类型  1:铂金  2:钻石
+        if (!is_numeric($type)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $type = intval($type);
+        if (!in_array($type, [1, 2])) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = $this->getUser();
+        $userVipPrice = Db::table('tb_user_vip_price')
+            ->where(['user_id' => $user->user_id])
+            ->where(['type'    => $type])
+            ->count();
+
+        $priceList = $type == 1 ? $this->platinumVip : $this->diamondVip;
+        if ($userVipPrice <= 0) {
+            //插入月付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 1,
+                    'price'    => $priceList[0],
+                ]);
+            //插入季付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 3,
+                    'price'    => $priceList[1],
+                ]);
+            //插入半年付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 6,
+                    'price'    => $priceList[2],
+                ]);
+            //插入一年付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 12,
+                    'price'    => $priceList[3],
+                ]);
+        }
+        $data = Db::table('tb_user_vip_price')
+            ->where(['user_id' => $user->user_id])
+            ->where(['type'    => $type])
+            ->select();
+        $this->success('success', $data);
+    }
+    
+    //购买开通VIP
+    //铂金VIP,包月20R,包季度54R,包半年96R,包年120R
+    //钻石VIP,包月30R,包季度81R,包半年144R,包年180R
+    public function buyVip()
+    {
+        $type = $this->request->post('type');               //VIP类型   1:铂金  2:钻石 
+        $duration = $this->request->post('duration');       //周期  按月1,3,6,12
+        if (!is_numeric($type) || !is_numeric($duration)) {
+            $this->fail(500, '参数校验错误');
+        }
+        $type = intval($type);
+        $duration = intval($duration);
+        if (!in_array($type, [1, 2]) || !in_array($duration, [1, 3, 6, 12])) {
+            $this->fail(500, '参数校验错误');
+        }
+        $user = $this->getUser();
+        $userVipPrice = Db::table('tb_user_vip_price')
+            ->where(['user_id' => $user->user_id])
+            ->where(['type'    => $type])
+            ->count();
+        $priceList = $type == 1 ? $this->platinumVip : $this->diamondVip;
+        if ($userVipPrice <= 0) {
+            //插入月付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 1,
+                    'price'    => $priceList[0],
+                ]);
+            //插入季付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 3,
+                    'price'    => $priceList[1],
+                ]);
+            //插入半年付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 6,
+                    'price'    => $priceList[2],
+                ]);
+            //插入一年付价格
+            Db::table('tb_user_vip_price')
+                ->insert([
+                    'user_id'  => $user->user_id,
+                    'type'     => $type,
+                    'duration' => 12,
+                    'price'    => $priceList[3],
+                ]);
+        }
+        $data = Db::table('tb_user_vip_price')
+            ->where(['user_id'  => $user->user_id])
+            ->where(['type'     => $type])
+            ->where(['duration' => $duration])
+            ->find();
+        $vipPrice = $data['price'];
+        $balanceUser = Db::table('tb_user')
+            ->where(['id' => $user->user_id])
+            ->find();
+        if ($balanceUser['balance'] < $vipPrice) {
+            $this->fail(501, '余额不足无法开通');
+        }
+        //扣除余额
+        Db::table('tb_user')
+            ->where(['id' => $user->user_id])
+            ->update([
+                'balance' => $balanceUser['balance'] - $vipPrice,
+            ]);
+        //开通VIP
+        $vip = Db::table('tb_user_vip')
+            ->where(['user_id' => $user->user_id])
+            ->find();
+        $startTime = 0;
+        $expiredTime = 0;
+        if (!$vip) {
+            $startTime = time();
+            $expiredTime = time() + 60 * 60 * 24 * 30 * $duration;
+            Db::table('tb_user_vip')
+                ->insert([
+                    'user_id'    => $user->user_id,
+                    'type'       => $type,
+                    'created_at' => time(),
+                    'expired_at' => time() + 60 * 60 * 24 * 30 * $duration,
+                ]);
+        } else {
+            $expired = false;
+            if ($vip['expired_at'] < time()) {
+                $expired = true;
+            }
+            $startTime = time();
+            $expiredTime = $expired ? time() + 60 * 60 * 24 * 30 * $duration : $vip['expired_at'] + 60 * 60 * 24 * 30 * $duration;
+            Db::table('tb_user_vip')
+                ->where(['user_id' => $user->user_id])
+                ->update([
+                    'type'       => $type,
+                    'expired_at' => $expired ? time() + 60 * 60 * 24 * 30 * $duration : $vip['expired_at'] + 60 * 60 * 24 * 30 * $duration,
+                ]);
+        }
+        //写VIP购买记录
+        Db::table('tb_user_vip_record')
+            ->insert([
+                'user_id'    => $user->user_id,
+                'price'      => $vipPrice,
+                'type'       => $type,
+                'duration'   => $duration,
+                'start_time' => $startTime,
+                'expired_at' => $expiredTime,
+                'created_at' => time(),
+            ]);
+        $this->success('success', null);
+    }
+}

+ 17 - 0
app/event.php
View File

@@ -0,0 +1,17 @@
+<?php
+// 事件定义文件
+return [
+    'bind'      => [
+    ],
+
+    'listen'    => [
+        'AppInit'  => [],
+        'HttpRun'  => [],
+        'HttpEnd'  => [],
+        'LogLevel' => [],
+        'LogWrite' => [],
+    ],
+
+    'subscribe' => [
+    ],
+];

+ 12 - 0
app/middleware.php
View File

@@ -0,0 +1,12 @@
+<?php
+// 全局中间件定义文件
+return [
+    // 全局请求缓存
+    // \think\middleware\CheckRequestCache::class,
+    // 多语言加载
+    // \think\middleware\LoadLangPack::class,
+    // Session初始化
+    \think\middleware\SessionInit::class,
+    // 跨域请求支持
+    \think\middleware\AllowCrossDomain::class,
+];

+ 9 - 0
app/provider.php
View File

@@ -0,0 +1,9 @@
+<?php
+use app\ExceptionHandle;
+use app\Request;
+
+// 容器Provider定义文件
+return [
+    'think\Request'          => Request::class,
+    'think\exception\Handle' => ExceptionHandle::class,
+];

+ 9 - 0
app/service.php
View File

@@ -0,0 +1,9 @@
+<?php
+
+use app\AppService;
+
+// 系统服务定义文件
+// 服务在完成全局初始化之后执行
+return [
+    AppService::class,
+];

+ 54 - 0
composer.json
View File

@@ -0,0 +1,54 @@
+{
+    "name": "topthink/think",
+    "description": "the new thinkphp framework",
+    "type": "project",
+    "keywords": [
+        "framework",
+        "thinkphp",
+        "ORM"
+    ],
+    "homepage": "https://www.thinkphp.cn/",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        },
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }        
+    ],
+    "require": {
+        "php": ">=7.2.5",
+        "topthink/framework": "^6.0.0",
+        "topthink/think-orm": "^2.0",
+        "predis/predis": "^3.2",
+        "ramsey/uuid": "^4.9",
+        "phpu/think-captcha": "^2.0",
+        "topthink/think-captcha": "^3.0",
+        "topthink/think-filesystem": "^2.0",
+        "phpmailer/phpmailer": "^6.10"
+    },
+    "require-dev": {
+        "symfony/var-dumper": "^4.2",
+        "topthink/think-trace":"^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "app\\": "app"
+        },
+        "psr-0": {
+            "": "extend/"
+        }
+    },
+    "config": {
+        "preferred-install": "dist"
+    },
+    "scripts": {
+        "post-autoload-dump": [
+            "@php think service:discover",
+            "@php think vendor:publish"
+        ]
+    }
+}

+ 1522 - 0
composer.lock
View File

@@ -0,0 +1,1522 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "56d1395049e978ed44640b52a6716112",
+    "packages": [
+        {
+            "name": "brick/math",
+            "version": "0.13.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/brick/math.git",
+                "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
+                "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpunit/phpunit": "^10.1",
+                "vimeo/psalm": "6.8.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Brick\\Math\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Arbitrary-precision arithmetic library",
+            "keywords": [
+                "Arbitrary-precision",
+                "BigInteger",
+                "BigRational",
+                "arithmetic",
+                "bigdecimal",
+                "bignum",
+                "bignumber",
+                "brick",
+                "decimal",
+                "integer",
+                "math",
+                "mathematics",
+                "rational"
+            ],
+            "support": {
+                "issues": "https://github.com/brick/math/issues",
+                "source": "https://github.com/brick/math/tree/0.13.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/BenMorel",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-03-29T13:50:30+00:00"
+        },
+        {
+            "name": "league/flysystem",
+            "version": "1.1.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem.git",
+                "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1",
+                "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "league/mime-type-detection": "^1.3",
+                "php": "^7.2.5 || ^8.0"
+            },
+            "conflict": {
+                "league/flysystem-sftp": "<1.0.6"
+            },
+            "require-dev": {
+                "phpspec/prophecy": "^1.11.1",
+                "phpunit/phpunit": "^8.5.8"
+            },
+            "suggest": {
+                "ext-ftp": "Allows you to use FTP server storage",
+                "ext-openssl": "Allows you to use FTPS server storage",
+                "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
+                "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
+                "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
+                "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
+                "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
+                "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
+                "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
+                "league/flysystem-webdav": "Allows you to use WebDAV storage",
+                "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
+                "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
+                "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frenky.net"
+                }
+            ],
+            "description": "Filesystem abstraction: Many filesystems, one API.",
+            "keywords": [
+                "Cloud Files",
+                "WebDAV",
+                "abstraction",
+                "aws",
+                "cloud",
+                "copy.com",
+                "dropbox",
+                "file systems",
+                "files",
+                "filesystem",
+                "filesystems",
+                "ftp",
+                "rackspace",
+                "remote",
+                "s3",
+                "sftp",
+                "storage"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem/issues",
+                "source": "https://github.com/thephpleague/flysystem/tree/1.1.10"
+            },
+            "funding": [
+                {
+                    "url": "https://offset.earth/frankdejonge",
+                    "type": "other"
+                }
+            ],
+            "time": "2022-10-04T09:16:37+00:00"
+        },
+        {
+            "name": "league/flysystem-cached-adapter",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
+                "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+                "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+                "shasum": ""
+            },
+            "require": {
+                "league/flysystem": "~1.0",
+                "psr/cache": "^1.0.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~0.9",
+                "phpspec/phpspec": "^3.4",
+                "phpunit/phpunit": "^5.7",
+                "predis/predis": "~1.0",
+                "tedivm/stash": "~0.12"
+            },
+            "suggest": {
+                "ext-phpredis": "Pure C implemented extension for PHP"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\Cached\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "frankdejonge",
+                    "email": "info@frenky.net"
+                }
+            ],
+            "description": "An adapter decorator to enable meta-data caching.",
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues",
+                "source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master"
+            },
+            "time": "2020-07-25T15:56:04+00:00"
+        },
+        {
+            "name": "league/mime-type-detection",
+            "version": "1.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/mime-type-detection.git",
+                "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9",
+                "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.2",
+                "phpstan/phpstan": "^0.12.68",
+                "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\MimeTypeDetection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "Mime-type detection for Flysystem",
+            "support": {
+                "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+                "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-21T08:32:55+00:00"
+        },
+        {
+            "name": "phpmailer/phpmailer",
+            "version": "v6.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPMailer/PHPMailer.git",
+                "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
+                "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-filter": "*",
+                "ext-hash": "*",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+                "doctrine/annotations": "^1.2.6 || ^1.13.3",
+                "php-parallel-lint/php-console-highlighter": "^1.0.0",
+                "php-parallel-lint/php-parallel-lint": "^1.3.2",
+                "phpcompatibility/php-compatibility": "^9.3.5",
+                "roave/security-advisories": "dev-latest",
+                "squizlabs/php_codesniffer": "^3.7.2",
+                "yoast/phpunit-polyfills": "^1.0.4"
+            },
+            "suggest": {
+                "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
+                "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
+                "ext-openssl": "Needed for secure SMTP sending and DKIM signing",
+                "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
+                "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
+                "league/oauth2-google": "Needed for Google XOAUTH2 authentication",
+                "psr/log": "For optional PSR-3 debug logging",
+                "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
+                "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PHPMailer\\PHPMailer\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1-only"
+            ],
+            "authors": [
+                {
+                    "name": "Marcus Bointon",
+                    "email": "phpmailer@synchromedia.co.uk"
+                },
+                {
+                    "name": "Jim Jagielski",
+                    "email": "jimjag@gmail.com"
+                },
+                {
+                    "name": "Andy Prevost",
+                    "email": "codeworxtech@users.sourceforge.net"
+                },
+                {
+                    "name": "Brent R. Matzelle"
+                }
+            ],
+            "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
+            "support": {
+                "issues": "https://github.com/PHPMailer/PHPMailer/issues",
+                "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Synchro",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-04-24T15:19:31+00:00"
+        },
+        {
+            "name": "phpu/think-captcha",
+            "version": "v2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/liujiawm/think-captcha.git",
+                "reference": "f94b1a441f3f0652ca89370ca1be910706096990"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/liujiawm/think-captcha/zipball/f94b1a441f3f0652ca89370ca1be910706096990",
+                "reference": "f94b1a441f3f0652ca89370ca1be910706096990",
+                "shasum": ""
+            },
+            "require": {
+                "ext-gd": "*",
+                "ext-mbstring": "*",
+                "php": ">=7.1.0",
+                "topthink/framework": "^6.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "think": {
+                    "config": {
+                        "phpu_captcha": "src/config.php"
+                    }
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpu\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "liujiawm",
+                    "email": "liujiawm@163.com"
+                }
+            ],
+            "description": "thinkphp6图片验证码",
+            "keywords": [
+                "captcha",
+                "phpu",
+                "think-captcha",
+                "thinkcaptcha",
+                "thinkphp"
+            ],
+            "support": {
+                "issues": "https://github.com/liujiawm/think-captcha/issues",
+                "source": "https://github.com/liujiawm/think-captcha/tree/v2.0.2"
+            },
+            "time": "2021-11-01T07:07:53+00:00"
+        },
+        {
+            "name": "predis/predis",
+            "version": "v3.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/predis/predis.git",
+                "reference": "9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/predis/predis/zipball/9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8",
+                "reference": "9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0",
+                "psr/http-message": "^1.0|^2.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.3",
+                "phpstan/phpstan": "^1.9",
+                "phpunit/phpcov": "^6.0 || ^8.0",
+                "phpunit/phpunit": "^8.0 || ~9.4.4"
+            },
+            "suggest": {
+                "ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Predis\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Till Krüss",
+                    "homepage": "https://till.im",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "A flexible and feature-complete Redis/Valkey client for PHP.",
+            "homepage": "http://github.com/predis/predis",
+            "keywords": [
+                "nosql",
+                "predis",
+                "redis"
+            ],
+            "support": {
+                "issues": "https://github.com/predis/predis/issues",
+                "source": "https://github.com/predis/predis/tree/v3.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/tillkruss",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-08-06T06:41:24+00:00"
+        },
+        {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/cache/tree/master"
+            },
+            "time": "2016-08-06T20:24:11+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+                "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.4.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/1.1.2"
+            },
+            "time": "2021-11-05T16:50:12+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+                "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/1.1"
+            },
+            "time": "2023-04-04T09:50:52+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/1.1.4"
+            },
+            "time": "2021-05-03T11:20:27+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/master"
+            },
+            "time": "2017-10-23T01:57:42+00:00"
+        },
+        {
+            "name": "ramsey/collection",
+            "version": "2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/collection.git",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "captainhook/plugin-composer": "^5.3",
+                "ergebnis/composer-normalize": "^2.45",
+                "fakerphp/faker": "^1.24",
+                "hamcrest/hamcrest-php": "^2.0",
+                "jangregor/phpstan-prophecy": "^2.1",
+                "mockery/mockery": "^1.6",
+                "php-parallel-lint/php-console-highlighter": "^1.0",
+                "php-parallel-lint/php-parallel-lint": "^1.4",
+                "phpspec/prophecy-phpunit": "^2.3",
+                "phpstan/extension-installer": "^1.4",
+                "phpstan/phpstan": "^2.1",
+                "phpstan/phpstan-mockery": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^10.5",
+                "ramsey/coding-standard": "^2.3",
+                "ramsey/conventional-commits": "^1.6",
+                "roave/security-advisories": "dev-latest"
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                },
+                "ramsey/conventional-commits": {
+                    "configFile": "conventional-commits.json"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Ramsey\\Collection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Ramsey",
+                    "email": "ben@benramsey.com",
+                    "homepage": "https://benramsey.com"
+                }
+            ],
+            "description": "A PHP library for representing and manipulating collections.",
+            "keywords": [
+                "array",
+                "collection",
+                "hash",
+                "map",
+                "queue",
+                "set"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/collection/issues",
+                "source": "https://github.com/ramsey/collection/tree/2.1.1"
+            },
+            "time": "2025-03-22T05:38:12+00:00"
+        },
+        {
+            "name": "ramsey/uuid",
+            "version": "4.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/uuid.git",
+                "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0",
+                "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0",
+                "shasum": ""
+            },
+            "require": {
+                "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
+                "php": "^8.0",
+                "ramsey/collection": "^1.2 || ^2.0"
+            },
+            "replace": {
+                "rhumsaa/uuid": "self.version"
+            },
+            "require-dev": {
+                "captainhook/captainhook": "^5.25",
+                "captainhook/plugin-composer": "^5.3",
+                "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+                "ergebnis/composer-normalize": "^2.47",
+                "mockery/mockery": "^1.6",
+                "paragonie/random-lib": "^2",
+                "php-mock/php-mock": "^2.6",
+                "php-mock/php-mock-mockery": "^1.5",
+                "php-parallel-lint/php-parallel-lint": "^1.4.0",
+                "phpbench/phpbench": "^1.2.14",
+                "phpstan/extension-installer": "^1.4",
+                "phpstan/phpstan": "^2.1",
+                "phpstan/phpstan-mockery": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^9.6",
+                "slevomat/coding-standard": "^8.18",
+                "squizlabs/php_codesniffer": "^3.13"
+            },
+            "suggest": {
+                "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+                "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+                "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+                "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Ramsey\\Uuid\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+            "keywords": [
+                "guid",
+                "identifier",
+                "uuid"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/uuid/issues",
+                "source": "https://github.com/ramsey/uuid/tree/4.9.0"
+            },
+            "time": "2025-06-25T14:20:11+00:00"
+        },
+        {
+            "name": "topthink/framework",
+            "version": "v6.1.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/framework.git",
+                "reference": "57d1950a1844ef8d3098ea290032aeb92e2e32c3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/framework/zipball/57d1950a1844ef8d3098ea290032aeb92e2e32c3",
+                "reference": "57d1950a1844ef8d3098ea290032aeb92e2e32c3",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "php": ">=7.2.5",
+                "psr/container": "~1.0",
+                "psr/http-message": "^1.0",
+                "psr/log": "~1.0",
+                "psr/simple-cache": "^1.0",
+                "topthink/think-helper": "^3.1.1",
+                "topthink/think-orm": "^2.0|^3.0"
+            },
+            "require-dev": {
+                "guzzlehttp/psr7": "^2.1.0",
+                "mikey179/vfsstream": "^1.6",
+                "mockery/mockery": "^1.2",
+                "phpunit/phpunit": "^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [],
+                "psr-4": {
+                    "think\\": "src/think/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "liu21st",
+                    "email": "liu21st@gmail.com"
+                },
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "The ThinkPHP Framework.",
+            "homepage": "http://thinkphp.cn/",
+            "keywords": [
+                "framework",
+                "orm",
+                "thinkphp"
+            ],
+            "support": {
+                "issues": "https://github.com/top-think/framework/issues",
+                "source": "https://github.com/top-think/framework/tree/v6.1.5"
+            },
+            "time": "2024-04-16T02:01:19+00:00"
+        },
+        {
+            "name": "topthink/think-captcha",
+            "version": "v3.0.11",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-captcha.git",
+                "reference": "4f24f560a31011329e3d144732e5370d7676b3fb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-captcha/zipball/4f24f560a31011329e3d144732e5370d7676b3fb",
+                "reference": "4f24f560a31011329e3d144732e5370d7676b3fb",
+                "shasum": ""
+            },
+            "require": {
+                "topthink/framework": "^6.0|^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "think": {
+                    "config": {
+                        "captcha": "src/config.php"
+                    },
+                    "services": [
+                        "think\\captcha\\CaptchaService"
+                    ]
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/helper.php"
+                ],
+                "psr-4": {
+                    "think\\captcha\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "captcha package for thinkphp",
+            "support": {
+                "issues": "https://github.com/top-think/think-captcha/issues",
+                "source": "https://github.com/top-think/think-captcha/tree/v3.0.11"
+            },
+            "time": "2024-11-22T12:59:35+00:00"
+        },
+        {
+            "name": "topthink/think-filesystem",
+            "version": "v2.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-filesystem.git",
+                "reference": "e8e51adb9f3a3f3aac2aa3ef73b7b439100f777d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-filesystem/zipball/e8e51adb9f3a3f3aac2aa3ef73b7b439100f777d",
+                "reference": "e8e51adb9f3a3f3aac2aa3ef73b7b439100f777d",
+                "shasum": ""
+            },
+            "require": {
+                "league/flysystem": "^1.1.4",
+                "league/flysystem-cached-adapter": "^1.0",
+                "php": ">=7.2.5",
+                "topthink/framework": "^6.1|^8.0"
+            },
+            "require-dev": {
+                "mikey179/vfsstream": "^1.6",
+                "mockery/mockery": "^1.2",
+                "phpunit/phpunit": "^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "think\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "The ThinkPHP6.1 Filesystem Package",
+            "support": {
+                "issues": "https://github.com/top-think/think-filesystem/issues",
+                "source": "https://github.com/top-think/think-filesystem/tree/v2.0.3"
+            },
+            "time": "2024-10-16T03:37:24+00:00"
+        },
+        {
+            "name": "topthink/think-helper",
+            "version": "v3.1.11",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-helper.git",
+                "reference": "1d6ada9b9f3130046bf6922fe1bd159c8d88a33c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-helper/zipball/1d6ada9b9f3130046bf6922fe1bd159c8d88a33c",
+                "reference": "1d6ada9b9f3130046bf6922fe1bd159c8d88a33c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/helper.php"
+                ],
+                "psr-4": {
+                    "think\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "yunwuxin",
+                    "email": "448901948@qq.com"
+                }
+            ],
+            "description": "The ThinkPHP6 Helper Package",
+            "support": {
+                "issues": "https://github.com/top-think/think-helper/issues",
+                "source": "https://github.com/top-think/think-helper/tree/v3.1.11"
+            },
+            "time": "2025-04-07T06:55:59+00:00"
+        },
+        {
+            "name": "topthink/think-orm",
+            "version": "v2.0.62",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-orm.git",
+                "reference": "e53bfea572a133039ad687077120de5521af617f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-orm/zipball/e53bfea572a133039ad687077120de5521af617f",
+                "reference": "e53bfea572a133039ad687077120de5521af617f",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-pdo": "*",
+                "php": ">=7.1.0",
+                "psr/log": "^1.0|^2.0",
+                "psr/simple-cache": "^1.0|^2.0",
+                "topthink/think-helper": "^3.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7|^8|^9.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "stubs/load_stubs.php"
+                ],
+                "psr-4": {
+                    "think\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "liu21st",
+                    "email": "liu21st@gmail.com"
+                }
+            ],
+            "description": "think orm",
+            "keywords": [
+                "database",
+                "orm"
+            ],
+            "support": {
+                "issues": "https://github.com/top-think/think-orm/issues",
+                "source": "https://github.com/top-think/think-orm/tree/v2.0.62"
+            },
+            "time": "2024-09-22T06:17:47+00:00"
+        }
+    ],
+    "packages-dev": [
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.32.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "shasum": ""
+            },
+            "require": {
+                "ext-iconv": "*",
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-12-23T08:48:59+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php72",
+            "version": "v1.31.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php72.git",
+                "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce",
+                "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "metapackage",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.31.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-09T11:45:10+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.32.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-01-02T08:10:11+00:00"
+        },
+        {
+            "name": "symfony/var-dumper",
+            "version": "v4.4.47",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "1069c7a3fca74578022fab6f81643248d02f8e63"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1069c7a3fca74578022fab6f81643248d02f8e63",
+                "reference": "1069c7a3fca74578022fab6f81643248d02f8e63",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php72": "~1.5",
+                "symfony/polyfill-php80": "^1.16"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+                "symfony/console": "<3.4"
+            },
+            "require-dev": {
+                "ext-iconv": "*",
+                "symfony/console": "^3.4|^4.0|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "twig/twig": "^1.43|^2.13|^3.0.4"
+            },
+            "suggest": {
+                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+                "ext-intl": "To show region name in time zone dump",
+                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+            },
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-dumper/tree/v4.4.47"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-10-03T15:15:11+00:00"
+        },
+        {
+            "name": "topthink/think-trace",
+            "version": "v1.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-trace.git",
+                "reference": "136cd5d97e8bdb780e4b5c1637c588ed7ca3e142"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-trace/zipball/136cd5d97e8bdb780e4b5c1637c588ed7ca3e142",
+                "reference": "136cd5d97e8bdb780e4b5c1637c588ed7ca3e142",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.0",
+                "topthink/framework": "^6.0|^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "think": {
+                    "config": {
+                        "trace": "src/config.php"
+                    },
+                    "services": [
+                        "think\\trace\\Service"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "think\\trace\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "liu21st",
+                    "email": "liu21st@gmail.com"
+                }
+            ],
+            "description": "thinkphp debug trace",
+            "support": {
+                "issues": "https://github.com/top-think/think-trace/issues",
+                "source": "https://github.com/top-think/think-trace/tree/v1.6"
+            },
+            "time": "2023-02-07T08:36:32+00:00"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=7.2.5"
+    },
+    "platform-dev": [],
+    "plugin-api-version": "2.0.0"
+}

+ 32 - 0
config/app.php
View File

@@ -0,0 +1,32 @@
+<?php
+// +----------------------------------------------------------------------
+// | 应用设置
+// +----------------------------------------------------------------------
+
+return [
+    // 应用地址
+    'app_host'         => env('app.host', ''),
+    // 应用的命名空间
+    'app_namespace'    => '',
+    // 是否启用路由
+    'with_route'       => true,
+    // 默认应用
+    'default_app'      => 'index',
+    // 默认时区
+    'default_timezone' => 'Asia/Shanghai',
+
+    // 应用映射(自动多应用模式有效)
+    'app_map'          => [],
+    // 域名绑定(自动多应用模式有效)
+    'domain_bind'      => [],
+    // 禁止URL访问的应用列表(自动多应用模式有效)
+    'deny_app_list'    => [],
+
+    // 异常页面的模板文件
+    'exception_tmpl'   => app()->getThinkPath() . 'tpl/think_exception.tpl',
+
+    // 错误显示信息,非调试模式有效
+    'error_message'    => '页面错误!请稍后再试~',
+    // 显示错误信息
+    'show_error_msg'   => false,
+];

+ 29 - 0
config/cache.php
View File

@@ -0,0 +1,29 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | 缓存设置
+// +----------------------------------------------------------------------
+
+return [
+    // 默认缓存驱动
+    'default' => env('cache.driver', 'file'),
+
+    // 缓存连接方式配置
+    'stores'  => [
+        'file' => [
+            // 驱动方式
+            'type'       => 'File',
+            // 缓存保存目录
+            'path'       => '',
+            // 缓存前缀
+            'prefix'     => '',
+            // 缓存有效期 0表示永久缓存
+            'expire'     => 0,
+            // 缓存标签前缀
+            'tag_prefix' => 'tag:',
+            // 序列化机制 例如 ['serialize', 'unserialize']
+            'serialize'  => [],
+        ],
+        // 更多的缓存连接
+    ],
+];

+ 43 - 0
config/captcha.php
View File

@@ -0,0 +1,43 @@
+<?php
+// +----------------------------------------------------------------------
+// | Captcha配置文件
+// +----------------------------------------------------------------------
+
+return [
+    //验证码位数
+    'length'   => 5,
+    // 验证码字符集合
+    'codeSet'  => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
+    // 验证码过期时间
+    'expire'   => 1800,
+    // 是否使用中文验证码
+    'useZh'    => false,
+    // 是否使用算术验证码
+    'math'     => false,
+    // 是否使用背景图
+    'useImgBg' => false,
+    //验证码字符大小
+    'fontSize' => 25,
+    // 是否使用混淆曲线
+    'useCurve' => true,
+    //是否添加杂点
+    'useNoise' => true,
+    // 验证码字体 不设置则随机
+    'fontttf'  => '',
+    //背景颜色
+    'bg'       => [243, 251, 254],
+    // 验证码图片高度
+    'imageH'   => 0,
+    // 验证码图片宽度
+    'imageW'   => 0,
+    // 验证码图片透明度
+    'alpha'    => 0,
+    // 是否采用API模式生成
+    'api'      => false,
+
+    // 添加额外的验证码设置
+    // verify => [
+    //     'length'=>4,
+    //    ...
+    //],
+];

+ 9 - 0
config/console.php
View File

@@ -0,0 +1,9 @@
+<?php
+// +----------------------------------------------------------------------
+// | 控制台配置
+// +----------------------------------------------------------------------
+return [
+    // 指令定义
+    'commands' => [
+    ],
+];

+ 20 - 0
config/cookie.php
View File

@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | Cookie设置
+// +----------------------------------------------------------------------
+return [
+    // cookie 保存时间
+    'expire'    => 0,
+    // cookie 保存路径
+    'path'      => '/',
+    // cookie 有效域名
+    'domain'    => '',
+    //  cookie 启用安全传输
+    'secure'    => false,
+    // httponly设置
+    'httponly'  => false,
+    // 是否使用 setcookie
+    'setcookie' => true,
+    // samesite 设置,支持 'strict' 'lax'
+    'samesite'  => '',
+];

+ 63 - 0
config/database.php
View File

@@ -0,0 +1,63 @@
+<?php
+
+return [
+    // 默认使用的数据库连接配置
+    'default'         => env('database.driver', 'mysql'),
+
+    // 自定义时间查询规则
+    'time_query_rule' => [],
+
+    // 自动写入时间戳字段
+    // true为自动识别类型 false关闭
+    // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
+    'auto_timestamp'  => true,
+
+    // 时间字段取出后的默认时间格式
+    'datetime_format' => 'Y-m-d H:i:s',
+
+    // 时间字段配置 配置格式:create_time,update_time
+    'datetime_field'  => '',
+
+    // 数据库连接配置信息
+    'connections'     => [
+        'mysql' => [
+            // 数据库类型
+            'type'            => env('database.type', 'mysql'),
+            // 服务器地址
+            'hostname'        => env('database.hostname', '127.0.0.1'),
+            // 数据库名
+            'database'        => env('database.database', ''),
+            // 用户名
+            'username'        => env('database.username', 'root'),
+            // 密码
+            'password'        => env('database.password', ''),
+            // 端口
+            'hostport'        => env('database.hostport', '3306'),
+            // 数据库连接参数
+            'params'          => [],
+            // 数据库编码默认采用utf8
+            'charset'         => env('database.charset', 'utf8'),
+            // 数据库表前缀
+            'prefix'          => env('database.prefix', ''),
+
+            // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+            'deploy'          => 0,
+            // 数据库读写是否分离 主从式有效
+            'rw_separate'     => false,
+            // 读写分离后 主服务器数量
+            'master_num'      => 1,
+            // 指定从服务器序号
+            'slave_no'        => '',
+            // 是否严格检查字段是否存在
+            'fields_strict'   => true,
+            // 是否需要断线重连
+            'break_reconnect' => false,
+            // 监听SQL
+            'trigger_sql'     => env('app_debug', true),
+            // 开启字段缓存
+            'fields_cache'    => false,
+        ],
+
+        // 更多的数据库配置信息
+    ],
+];

+ 24 - 0
config/filesystem.php
View File

@@ -0,0 +1,24 @@
+<?php
+
+return [
+    // 默认磁盘
+    'default' => env('filesystem.driver', 'local'),
+    // 磁盘列表
+    'disks'   => [
+        'local'  => [
+            'type' => 'local',
+            'root' => app()->getRuntimePath() . 'storage',
+        ],
+        'public' => [
+            // 磁盘类型
+            'type'       => 'local',
+            // 磁盘路径
+            'root'       => app()->getRootPath() . 'public/storage',
+            // 磁盘路径对应的外部URL路径
+            'url'        => '/storage',
+            // 可见性
+            'visibility' => 'public',
+        ],
+        // 更多的磁盘配置信息
+    ],
+];

+ 27 - 0
config/lang.php
View File

@@ -0,0 +1,27 @@
+<?php
+// +----------------------------------------------------------------------
+// | 多语言设置
+// +----------------------------------------------------------------------
+
+return [
+    // 默认语言
+    'default_lang'    => env('lang.default_lang', 'zh-cn'),
+    // 允许的语言列表
+    'allow_lang_list' => [],
+    // 多语言自动侦测变量名
+    'detect_var'      => 'lang',
+    // 是否使用Cookie记录
+    'use_cookie'      => true,
+    // 多语言cookie变量
+    'cookie_var'      => 'think_lang',
+    // 多语言header变量
+    'header_var'      => 'think-lang',
+    // 扩展语言包
+    'extend_list'     => [],
+    // Accept-Language转义为对应语言包名称
+    'accept_language' => [
+        'zh-hans-cn' => 'zh-cn',
+    ],
+    // 是否支持语言分组
+    'allow_group'     => false,
+];

+ 45 - 0
config/log.php
View File

@@ -0,0 +1,45 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | 日志设置
+// +----------------------------------------------------------------------
+return [
+    // 默认日志记录通道
+    'default'      => env('log.channel', 'file'),
+    // 日志记录级别
+    'level'        => [],
+    // 日志类型记录的通道 ['error'=>'email',...]
+    'type_channel' => [],
+    // 关闭全局日志写入
+    'close'        => false,
+    // 全局日志处理 支持闭包
+    'processor'    => null,
+
+    // 日志通道列表
+    'channels'     => [
+        'file' => [
+            // 日志记录方式
+            'type'           => 'File',
+            // 日志保存目录
+            'path'           => '',
+            // 单文件日志写入
+            'single'         => false,
+            // 独立日志级别
+            'apart_level'    => [],
+            // 最大日志文件数量
+            'max_files'      => 0,
+            // 使用JSON格式记录
+            'json'           => false,
+            // 日志处理
+            'processor'      => null,
+            // 关闭通道日志写入
+            'close'          => false,
+            // 日志输出格式化
+            'format'         => '[%s][%s] %s',
+            // 是否实时写入
+            'realtime_write' => false,
+        ],
+        // 其它日志通道配置
+    ],
+
+];

+ 8 - 0
config/middleware.php
View File

@@ -0,0 +1,8 @@
+<?php
+// 中间件配置
+return [
+    // 别名或分组
+    'alias'    => [],
+    // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
+    'priority' => [],
+];

+ 29 - 0
config/phpu_captcha.php
View File

@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * 验证码配置,如果需要对某验证码单独配置,可以按‘配置名’=>[]的方式设置
+ */
+return [
+    // 默认配置
+    'default' => [
+        'char_preset' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', // 预设字符集,不支持多字节字符
+        'length'     => 5, // 验证码位数
+        'width'      => 0, // 图片宽
+        'height'     => 0, // 图片高
+        'font_size'   => 48, // 验证码字体大小(px)
+        'bg'         => [243, 251, 254], // 背景颜色
+        'use_curve'   => true, // 是否画混淆曲线
+        'use_noise'   => true, // 是否添加杂点
+        'use_img_bg'   => true, // 是否使用背景图片
+    ],
+
+    // 独立配置
+    'sign' => [
+        'char_preset' => '0123456789', // 预设字符集
+        'length'     => 4, // 验证码位数
+        'width'      => 100, // 图片宽
+        'height'     => 36, // 图片高
+        'font_size'   => 24, // 验证码字体大小(px)
+        'use_img_bg'   => false, // 是否使用背景图片
+    ],
+];

+ 45 - 0
config/route.php
View File

@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | 路由设置
+// +----------------------------------------------------------------------
+
+return [
+    // pathinfo分隔符
+    'pathinfo_depr'         => '/',
+    // URL伪静态后缀
+    'url_html_suffix'       => 'html',
+    // URL普通方式参数 用于自动生成
+    'url_common_param'      => true,
+    // 是否开启路由延迟解析
+    'url_lazy_route'        => false,
+    // 是否强制使用路由
+    'url_route_must'        => false,
+    // 合并路由规则
+    'route_rule_merge'      => false,
+    // 路由是否完全匹配
+    'route_complete_match'  => false,
+    // 访问控制器层名称
+    'controller_layer'      => 'controller',
+    // 空控制器名
+    'empty_controller'      => 'Error',
+    // 是否使用控制器后缀
+    'controller_suffix'     => false,
+    // 默认的路由变量规则
+    'default_route_pattern' => '[\w\.]+',
+    // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+    'request_cache_key'     => false,
+    // 请求缓存有效期
+    'request_cache_expire'  => null,
+    // 全局请求缓存排除规则
+    'request_cache_except'  => [],
+    // 默认控制器名
+    'default_controller'    => 'Index',
+    // 默认操作名
+    'default_action'        => 'index',
+    // 操作方法后缀
+    'action_suffix'         => '',
+    // 默认JSONP格式返回的处理方法
+    'default_jsonp_handler' => 'jsonpReturn',
+    // 默认JSONP处理方法
+    'var_jsonp_handler'     => 'callback',
+];

+ 19 - 0
config/session.php
View File

@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | 会话设置
+// +----------------------------------------------------------------------
+
+return [
+    // session name
+    'name'           => 'PHPSESSID',
+    // SESSION_ID的提交变量,解决flash上传跨域
+    'var_session_id' => '',
+    // 驱动方式 支持file cache
+    'type'           => 'file',
+    // 存储连接标识 当type使用cache的时候有效
+    'store'          => null,
+    // 过期时间
+    'expire'         => 1440,
+    // 前缀
+    'prefix'         => '',
+];

+ 10 - 0
config/trace.php
View File

@@ -0,0 +1,10 @@
+<?php
+// +----------------------------------------------------------------------
+// | Trace设置 开启调试模式后有效
+// +----------------------------------------------------------------------
+return [
+    // 内置Html和Console两种方式 支持扩展
+    'type'    => 'Html',
+    // 读取的日志通道名
+    'channel' => '',
+];

+ 25 - 0
config/view.php
View File

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | 模板设置
+// +----------------------------------------------------------------------
+
+return [
+    // 模板引擎类型使用Think
+    'type'          => 'Think',
+    // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+    'auto_rule'     => 1,
+    // 模板目录名
+    'view_dir_name' => 'view',
+    // 模板后缀
+    'view_suffix'   => 'html',
+    // 模板文件名分隔符
+    'view_depr'     => DIRECTORY_SEPARATOR,
+    // 模板引擎普通标签开始标记
+    'tpl_begin'     => '{',
+    // 模板引擎普通标签结束标记
+    'tpl_end'       => '}',
+    // 标签库标签开始标记
+    'taglib_begin'  => '{',
+    // 标签库标签结束标记
+    'taglib_end'    => '}',
+];

+ 2 - 0
extend/.gitignore
View File

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 39 - 0
index.html
View File

@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>恭喜,站点创建成功!</title>
+    <style>
+        .container {
+            width: 60%;
+            margin: 10% auto 0;
+            background-color: #f0f0f0;
+            padding: 2% 5%;
+            border-radius: 10px
+        }
+
+        ul {
+            padding-left: 20px;
+        }
+
+            ul li {
+                line-height: 2.3
+            }
+
+        a {
+            color: #20a53a
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <h1>恭喜, 站点创建成功!</h1>
+        <h3>这是默认index.html,本页面由系统自动生成</h3>
+        <ul>
+            <li>本页面在FTP根目录下的index.html</li>
+            <li>您可以修改、删除或覆盖本页面</li>
+            <li>FTP相关信息,请到“面板系统后台 > FTP” 查看</li>
+        </ul>
+    </div>
+</body>
+</html>

+ 563 - 0
mysql/thinkphp6_template.sql
View File

@@ -0,0 +1,563 @@
+/*
+ Navicat Premium Data Transfer
+
+ Source Server         : localhost
+ Source Server Type    : MySQL
+ Source Server Version : 50744
+ Source Host           : localhost:3306
+ Source Schema         : thinkphp6_template
+
+ Target Server Type    : MySQL
+ Target Server Version : 50744
+ File Encoding         : 65001
+
+ Date: 18/09/2025 15:02:40
+*/
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for tb_admin
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_admin`;
+CREATE TABLE `tb_admin`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
+  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',
+  `rule_id` int(11) NOT NULL COMMENT '权限组id',
+  `created_at` int(11) NULL DEFAULT NULL COMMENT '创建时间',
+  `updated_at` int(11) NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_admin_login_log
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_admin_login_log`;
+CREATE TABLE `tb_admin_login_log`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL,
+  `user_agent` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `created_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 108 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_admin_token
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_admin_token`;
+CREATE TABLE `tb_admin_token`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL,
+  `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `expired_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 103 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_cdkey
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_cdkey`;
+CREATE TABLE `tb_cdkey`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `cdkey` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '兑换码',
+  `game_id` int(11) NOT NULL COMMENT '游戏ID',
+  `expired_at` int(11) NOT NULL COMMENT '过期时间',
+  `user_id` int(11) NULL DEFAULT NULL COMMENT '使用兑换码用户ID',
+  `state` int(11) NOT NULL COMMENT '状态 0:未使用 1:已使用 2:已过期',
+  `use_at` int(11) NULL DEFAULT NULL COMMENT '使用时间',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_email_captcha
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_email_captcha`;
+CREATE TABLE `tb_email_captcha`  (
+  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `event` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '事件',
+  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '邮箱',
+  `code` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '验证码',
+  `times` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '验证次数',
+  `ip` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'IP',
+  `createtime` bigint(16) NULL DEFAULT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 328 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '邮箱验证码表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_feedback
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_feedback`;
+CREATE TABLE `tb_feedback`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NULL DEFAULT NULL COMMENT '用户ID',
+  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '投诉/建议内容',
+  `reply_content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '回复内容',
+  `is_reply` int(11) NULL DEFAULT 0 COMMENT '是否回复 0未回复 1已回复',
+  `create_at` int(11) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_at` int(11) NULL DEFAULT NULL COMMENT '回复时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '投诉反馈建议表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_forum_category
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_forum_category`;
+CREATE TABLE `tb_forum_category`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `category_id` int(255) NOT NULL COMMENT '分类id',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '版块名称',
+  `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '版块描述',
+  `image_url` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '图片URL',
+  `admin_id` int(11) NOT NULL COMMENT '版主用户ID',
+  `priority` int(11) NOT NULL COMMENT '排序优先级,越小越靠前',
+  `created_at` int(11) NULL DEFAULT NULL COMMENT '创建时间',
+  `updated_at` int(11) NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_forum_category_notice
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_forum_category_notice`;
+CREATE TABLE `tb_forum_category_notice`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `category_id` int(11) NOT NULL COMMENT '版块ID',
+  `notice_url` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告URL',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_forum_thread_category
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_forum_thread_category`;
+CREATE TABLE `tb_forum_thread_category`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `category_id` int(11) NOT NULL COMMENT '版块ID',
+  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '分类标题',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_friend
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_friend`;
+CREATE TABLE `tb_friend`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `friend_id` int(11) NOT NULL COMMENT '好友用户ID',
+  `state` int(11) NOT NULL COMMENT '状态 0:等待验证 1:已通过 2:已拒绝',
+  `msg` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '好友验证消息',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  `expired_at` int(11) NOT NULL COMMENT '验证过期时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_fuzhu
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_fuzhu`;
+CREATE TABLE `tb_fuzhu`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '辅助标题',
+  `image_url` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '辅助图标图片URL',
+  `version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '辅助版本',
+  `file_size` int(11) NOT NULL COMMENT '辅助文件大小(字节)',
+  `language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '辅助语言',
+  `game` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '游戏名称',
+  `type` int(11) NOT NULL COMMENT '授权类型 0:免费辅助 1:收费辅助',
+  `priority` int(11) NOT NULL COMMENT '排序优先级,越小越靠前原则',
+  `rate` int(11) NOT NULL COMMENT '评分星星数,最多5分',
+  `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '辅助详细描述',
+  `download_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '下载地址URL',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_fuzhu_download
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_fuzhu_download`;
+CREATE TABLE `tb_fuzhu_download`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `fuzhu_id` int(11) NOT NULL COMMENT '辅助ID',
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_fuzhu_vote
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_fuzhu_vote`;
+CREATE TABLE `tb_fuzhu_vote`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `fuzhu_id` int(11) NOT NULL COMMENT '辅助ID',
+  `value` int(11) NOT NULL COMMENT '投票值 0:踩 1:赞',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_game
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_game`;
+CREATE TABLE `tb_game`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `category` int(11) NOT NULL COMMENT '分类ID',
+  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品标题',
+  `image_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '图片URL',
+  `price` float NOT NULL COMMENT '价格',
+  `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '描述',
+  `priority` int(11) NOT NULL COMMENT '优先级,越小越靠前',
+  `browse` int(11) NOT NULL COMMENT '浏览次数',
+  `buy_count` int(11) NOT NULL COMMENT '购买次数',
+  `game_version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '游戏版本',
+  `language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '游戏语言',
+  `download_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '下载链接',
+  `extracted_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '提取码',
+  `created_at` int(11) NOT NULL,
+  `updated_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 44 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_game_bind_record
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_game_bind_record`;
+CREATE TABLE `tb_game_bind_record`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `game_id` int(11) NOT NULL COMMENT '游戏ID',
+  `machine_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '机器码',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_game_category
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_game_category`;
+CREATE TABLE `tb_game_category`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `image_url` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `priority` int(11) NOT NULL COMMENT '优先级,越大排名越靠前',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_game_machine
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_game_machine`;
+CREATE TABLE `tb_game_machine`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `game_id` int(11) NOT NULL COMMENT '游戏ID',
+  `machine_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '机器码',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `expired_at` int(11) NOT NULL COMMENT '到期时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_global_config
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_global_config`;
+CREATE TABLE `tb_global_config`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '配置名称',
+  `value` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '配置值',
+  `description` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '配置描述',
+  `create_at` int(11) NULL DEFAULT NULL COMMENT '创建时间',
+  `update_at` int(11) NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '全局配置表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_kefu
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_kefu`;
+CREATE TABLE `tb_kefu`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `jump_url` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `type` int(11) NOT NULL COMMENT '客服类型: 1:QQ客服 2:微信客服 3:Telegram客服',
+  `created_at` int(11) NULL DEFAULT NULL,
+  `updated_at` int(11) NULL DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_message
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_message`;
+CREATE TABLE `tb_message`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `from_uid` int(11) NOT NULL COMMENT '来自用户ID',
+  `to_uid` int(11) NOT NULL COMMENT '发送到用户ID',
+  `msg` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '消息内容',
+  `is_read` int(11) NOT NULL COMMENT '是否已读1:已读 0:未读',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `read_at` int(11) NULL DEFAULT NULL COMMENT '读取时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_search_keywords
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_search_keywords`;
+CREATE TABLE `tb_search_keywords`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `keywords` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `created_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 761 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_system_avatar
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_system_avatar`;
+CREATE TABLE `tb_system_avatar`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `image_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '图片URL',
+  `created_at` int(11) NULL DEFAULT NULL COMMENT '创建时间',
+  `updated_at` int(11) NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_thread
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_thread`;
+CREATE TABLE `tb_thread`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `category_id` int(11) NOT NULL COMMENT '版块ID',
+  `thread_category` int(11) NOT NULL COMMENT '帖子分类ID',
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '帖子标题',
+  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '帖子内容',
+  `browse` int(11) NOT NULL COMMENT '浏览次数',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_thread_reply
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_thread_reply`;
+CREATE TABLE `tb_thread_reply`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `thread_id` int(11) NOT NULL COMMENT '帖子ID',
+  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_thread_visit
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_thread_visit`;
+CREATE TABLE `tb_thread_visit`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID,如果登录后访问,显示用户ID,访客显示0',
+  `is_user` int(11) NOT NULL COMMENT '是否注册会员 1:是 0:不是',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1747 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user`;
+CREATE TABLE `tb_user`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
+  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `last_login` int(11) NULL DEFAULT NULL COMMENT '上次登录时间',
+  `login_count` int(11) NOT NULL COMMENT '登录次数',
+  `last_login_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '上次登录IP',
+  `avatar` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '头像URL',
+  `state` int(11) NOT NULL COMMENT '状态  0:未激活  1:正常   2:封号',
+  `balance` float NOT NULL COMMENT '余额',
+  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱账号',
+  `mobile` int(11) NULL DEFAULT NULL COMMENT '手机号',
+  `is_email_verified` int(11) NULL DEFAULT NULL COMMENT '邮箱认证状态 0:未认证   1:已认证',
+  `score` int(11) NULL DEFAULT NULL COMMENT '积分数量',
+  `gold` int(11) NULL DEFAULT NULL COMMENT '金币数量',
+  `exp` int(11) NOT NULL COMMENT '经验值',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_balance_record
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_balance_record`;
+CREATE TABLE `tb_user_balance_record`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL,
+  `amount` int(11) NOT NULL,
+  `created_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_bug
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_bug`;
+CREATE TABLE `tb_user_bug`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NULL DEFAULT NULL COMMENT '用户ID',
+  `type` int(11) NULL DEFAULT NULL COMMENT 'Bug类型 1单机游戏 2收费辅助',
+  `game_id` int(11) NULL DEFAULT NULL COMMENT '游戏ID',
+  `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'Bug反馈正文',
+  `is_reply` int(11) NULL DEFAULT 0 COMMENT '是否回复 0否 1是',
+  `reply_content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '回复正文',
+  `create_at` int(11) NULL DEFAULT NULL COMMENT 'Bug反馈时间',
+  `update_at` int(11) NULL DEFAULT NULL COMMENT '回复时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Bug反馈表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_game
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_game`;
+CREATE TABLE `tb_user_game`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL,
+  `game_id` int(11) NOT NULL,
+  `price` float NOT NULL COMMENT '购买时价格',
+  `channel` int(11) NOT NULL COMMENT '购买渠道 1:站内直购 2:淘宝 3:拼多多',
+  `created_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_group
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_group`;
+CREATE TABLE `tb_user_group`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户组名称',
+  `level` int(11) NOT NULL COMMENT '等级',
+  `exp` int(11) NOT NULL COMMENT '所需经验值',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `updated_at` int(11) NOT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_login_log
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_login_log`;
+CREATE TABLE `tb_user_login_log`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录IP地址',
+  `user_agent` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'USER-AGENT',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 237 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_order
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_order`;
+CREATE TABLE `tb_user_order`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `order_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单号',
+  `balance` float NOT NULL COMMENT '订单金额',
+  `state` int(11) NOT NULL COMMENT '订单状态 0:待支付 1:已支付 3:订单超时',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `pay_at` int(11) NULL DEFAULT NULL COMMENT '支付完成时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 63 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_token
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_token`;
+CREATE TABLE `tb_user_token`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `user_id` int(11) NOT NULL,
+  `expired_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 237 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_vip
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_vip`;
+CREATE TABLE `tb_user_vip`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `type` int(11) NOT NULL COMMENT 'VIP类型 1:铂金VIP  2:钻石VIP',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  `expired_at` int(11) NOT NULL COMMENT '到期时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_vip_price
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_vip_price`;
+CREATE TABLE `tb_user_vip_price`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `type` int(11) NOT NULL COMMENT '类型',
+  `duration` int(11) NOT NULL COMMENT '购买周期',
+  `price` int(11) NOT NULL COMMENT '价格',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_user_vip_record
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_user_vip_record`;
+CREATE TABLE `tb_user_vip_record`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '用户ID',
+  `price` int(11) NOT NULL COMMENT '购买时价格',
+  `type` int(11) NOT NULL COMMENT 'VIP类型:1:铂金VIP 2:钻石VIP',
+  `duration` int(11) NOT NULL COMMENT '购买周期(月)',
+  `start_time` int(11) NOT NULL COMMENT '开始时间',
+  `expired_at` int(11) NOT NULL COMMENT '到期时间',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for tb_website_visit
+-- ----------------------------
+DROP TABLE IF EXISTS `tb_website_visit`;
+CREATE TABLE `tb_website_visit`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `ip_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'IP地址',
+  `is_user` int(11) NOT NULL COMMENT '是否注册用户',
+  `user_id` int(11) NULL DEFAULT NULL COMMENT '用户ID,如没有为NULL',
+  `user_agent` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'user-agent',
+  `created_at` int(11) NOT NULL COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1683 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+SET FOREIGN_KEY_CHECKS = 1;

+ 8 - 0
public/.htaccess
View File

@@ -0,0 +1,8 @@
+<IfModule mod_rewrite.c>
+  Options +FollowSymlinks -Multiviews
+  RewriteEngine On
+
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+</IfModule>

+ 1 - 0
public/.user.ini
View File

@@ -0,0 +1 @@
+open_basedir=/www/wwwroot/api.danjiwanjia.com/danjiwanjia_api/:/tmp/

+ 1 - 0
public/.well-known/acme-challenge/fm7DNGpS5Fo2HnpsNXujRd7W9laiKAZpqzPbifJNkRQ
View File

@@ -0,0 +1 @@
+fm7DNGpS5Fo2HnpsNXujRd7W9laiKAZpqzPbifJNkRQ.pJZh7cHjhxR4HNTO0YzGfMzFhvrzkck19FaZIG6xcS4

+ 81 - 0
public/aa.html
View File

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8" />
+    <!-- import CSS -->
+    <link
+      rel="stylesheet"
+      href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
+    />
+  </head>
+  <body>
+    <div id="app">
+      <el-card style="width: 800px">
+        <el-form>
+          <el-form-item label="Username">
+            <el-input v-model="form.username"></el-input>
+          </el-form-item>
+          <el-form-item label="Password">
+            <el-input
+              type="password"
+              v-model="form.password"
+              autocomplete="off"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="Avatar">
+            <el-input type="text" v-model="form.avatar"></el-input>
+          </el-form-item>
+          <el-form-item label="Email">
+            <el-input type="text" v-model="form.email"></el-input>
+          </el-form-item>
+          <el-form-item label="Captcha">
+            <br />
+            <div style="display: flex">
+              <el-input type="text" v-model="form.captcha"></el-input>
+              <img
+                src="https://api.danjiwanjia.com/user/getRegisterCaptchaImage"
+                alt="Captcha"
+              />
+            </div>
+          </el-form-item>
+          <el-form-item>
+            <el-button @click="submitForm">Submit</el-button>
+          </el-form-item>
+        </el-form>
+      </el-card>
+    </div>
+  </body>
+  <!-- import Vue before Element -->
+  <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
+  <!-- import JavaScript -->
+  <script src="https://unpkg.com/element-ui/lib/index.js"></script>
+  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+  <script>
+    let v = new Vue({
+      el: "#app",
+      data() {
+        return {
+          form: {
+            username: "",
+            password: "",
+            avatar: "",
+            email: "",
+            captcha: "",
+          },
+        };
+      },
+      methods: {
+        submitForm() {
+          axios
+            .post("https://api.danjiwanjia.com/user/register", this.form)
+            .then((response) => {
+              console.log("Registration successful:", response.data);
+            })
+            .catch((error) => {
+              console.error("Registration failed:", error);
+            });
+        },
+      },
+    });
+  </script>
+</html>

BIN
public/favicon.ico
View File


+ 30 - 0
public/index.php
View File

@@ -0,0 +1,30 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+// 添加允许跨域请求头
+header("'Access-Control-Allow-Credentials: true");
+header("Access-Control-Allow-Origin: *");
+header('Access-Control-Allow-Methods: *');
+header("Access-Control-Allow-Headers: token");
+
+// 执行HTTP应用并响应
+$http = (new App())->http;
+
+$response = $http->run();
+
+$response->send();
+
+$http->end($response);

+ 2 - 0
public/robots.txt
View File

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:

+ 19 - 0
public/router.php
View File

@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+// $Id$
+
+if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
+    return false;
+} else {
+    $_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
+
+    require __DIR__ . "/index.php";
+}

+ 2 - 0
public/static/.gitignore
View File

@@ -0,0 +1,2 @@
+*
+!.gitignore

BIN
public/storage/attachments/20250828/18e86ee37f9ee18a6034d5f2d0a80f7e.zip
View File


BIN
public/storage/attachments/20250828/210f5d2ca5c973a225edfbd979e91cf7.zip
View File


BIN
public/storage/attachments/20250828/29034430d609b0d7a97a57e51305d715.zip
View File


BIN
public/storage/attachments/20250828/37dc4d9c49de2aaa63c8829fadda5179.zip
View File


BIN
public/storage/attachments/20250828/381621b994066abca8214a7aa2c59674.zip
View File


BIN
public/storage/attachments/20250828/540ebbc8e311a74efdc02b410e26be4c.zip
View File


BIN
public/storage/attachments/20250828/5a5ed68bf0d80e6ee32ae5b1ac7d1604.zip
View File


BIN
public/storage/attachments/20250828/9f919cf4bccae5567d573cb8d2adb22d.zip
View File


BIN
public/storage/attachments/20250828/a1c628104980044fb90b784f5e1e88c4.zip
View File


BIN
public/storage/attachments/20250828/a97239d9cc55597e04d44a225e56cd04.zip
View File


BIN
public/storage/attachments/20250828/bdd4afac1ad873d7b40065c3a674848b.zip
View File


BIN
public/storage/attachments/20250828/dbe539f238e3d7b1dcfb9d9b93035434.zip
View File


BIN
public/storage/attachments/20250828/dc1bd82443efe20e276b7fc51934498a.zip
View File


BIN
public/storage/avatar/20250812/3e4d666f44ece1335ce98633a38c8df1.png
View File


BIN
public/storage/avatar/20250813/c87b94bdfa34a13e9f121625111a8a38.png
View File


BIN
public/storage/avatar/20250813/ca1400dc7de4ae08a5923e698111dfbf.png
View File


BIN
public/storage/avatar/20250813/dabbe83db838841387684f3a0064a281.png
View File


BIN
public/storage/avatar/20250909/4fecbdf5190363f3f1d6204caab0aefb.png
View File


BIN
public/storage/images/20250826/3eea75d4856edc1005d76932f2d33742.png
View File


BIN
public/storage/images/20250826/50097c4598e51f5840a65d0b2c11ec3e.png
View File


BIN
public/storage/images/20250826/5af796bfc73024eed5ccf7bc778a991d.png
View File


BIN
public/storage/images/20250826/e144336c4665d32b6993e0d3852a4cb6.png
View File


BIN
public/storage/images/20250828/44aa7ab18ec5713b4fb843d90376b173.png
View File


BIN
public/storage/images/20250828/630fff6b8b2eb73a2e0bc07248f76615.png
View File


BIN
public/storage/images/20250828/be278a6849f12adc73c00dca24732a00.png
View File


BIN
public/storage/images/20250828/c19fd410b35c32c29b7f555cbe9266a3.png
View File


BIN
public/storage/images/20250828/e3c65039aef4c0a49c9a180d050ea40e.png
View File


BIN
public/storage/images/20250828/f4bc03a6f519f6519f65405cbd87df86.png
View File


BIN
public/storage/images/20250903/0806ec92f46220f5e05f0923ed82285c.png
View File


BIN
public/storage/images/20250903/182a451982f7c6595052b4e06791678b.png
View File


BIN
public/storage/images/20250903/39c90e35c7d53132053fdc6f66e175a6.png
View File


BIN
public/storage/images/20250903/6314d2671a381bd699a09c5218132dba.png
View File


BIN
public/storage/images/20250903/7b6893e23192ce1c698f3efc113889fa.png
View File


BIN
public/storage/images/20250903/9387776cdec87b95e27ba20c193f63f0.png
View File


BIN
public/storage/images/20250903/9a39132e199d12436ced9304071ef0d8.png
View File


BIN
public/storage/images/20250903/9a8580d6b4af52047cc95d161d803363.png
View File


BIN
public/storage/images/20250903/af35c91d67e9d55fc0408c6f29e3092c.png
View File


BIN
public/storage/images/20250903/d2079b48ac384fabdb6d20cfb3ade3e9.png
View File


BIN
public/storage/images/20250903/d23b791adcbb45937c0ad7e05418cf8c.png
View File


Some files were not shown because too many files changed in this diff