From dd62e2cdd1ea21908436f6050ca0931dc9ac3a90 Mon Sep 17 00:00:00 2001
From: zhaoxiang <zhaoxiang051405@gmail.com>
Date: Mon, 12 Oct 2020 16:16:37 +0800
Subject: [PATCH] =?UTF-8?q?modified=20=E5=9F=BA=E7=A1=80=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md                           |  140 +++-
 app/controller/admin/Menu.php       |  112 +++
 app/controller/admin/ThirdLogin.php |  350 +++++++++
 composer.json                       |    7 +-
 composer.lock                       | 1131 +++++++++++++++++++++++----
 5 files changed, 1558 insertions(+), 182 deletions(-)
 create mode 100644 app/controller/admin/Menu.php
 create mode 100644 app/controller/admin/ThirdLogin.php

diff --git a/README.md b/README.md
index d8ff638..9c79c60 100644
--- a/README.md
+++ b/README.md
@@ -1,56 +1,122 @@
-ThinkPHP 6.0
-===============
+> 站在巨人的肩膀上,并不是高的表现,反而使自己变得渺小~只有吸收了巨人的营养,茁壮自己才是真正的高大! --笔者
 
-> 运行环境要求PHP7.1+。
 
-[官方应用服务市场](https://www.thinkphp.cn/service) | [`ThinkPHP`开发者扶持计划](https://sites.thinkphp.cn/1782366)
+# ApiAdmin
+[![ApiAdmin](https://img.shields.io/hexpm/l/plug.svg)](http://www.apiadmin.org/)
+[![ApiAdmin](https://img.shields.io/badge/release-5.0.0-blue.svg)](http://www.apiadmin.org/)
+[![ApiAdmin](https://img.shields.io/badge/build-passing-brightgreen.svg)](http://www.apiadmin.org/)
+[![ApiAdmin](https://img.shields.io/badge/ApiAdmin-5.0.0-brightgreen.svg)](http://www.apiadmin.org/)
 
-ThinkPHPV6.0版本由[亿速云](https://www.yisu.com/)独家赞助发布。
+## 前端页面
+ApiAdmin4.1是一个前后端完全分离的项目,前端采用Vue构建,如需要可视化配置的请移步:[ApiAdmin-WEB](https://gitee.com/apiadmin/ApiAdmin-WEB)
 
-## 主要新特性
+## 快速安装
 
-* 采用`PHP7`强类型(严格模式)
-* 支持更多的`PSR`规范
-* 原生多应用支持
-* 更强大和易用的查询
-* 全新的事件系统
-* 模型事件和数据库事件统一纳入事件系统
-* 模板引擎分离出核心
-* 内部功能中间件化
-* SESSION/Cookie机制改进
-* 对Swoole以及协程支持改进
-* 对IDE更加友好
-* 统一和精简大量用法
+> 第一步:安装代码
 
-## 安装
+```
+composer create-project apiadmin/apiadmin5
+```
+```
+你也可以:先获取基础代码 git clone https://gitee.com/apiadmin/ApiAdmin.git   再使用composer安装 composer install
+```
 
-~~~
-composer create-project topthink/think tp 6.0.*
-~~~
+> 第二步:检测环境以及配置数据库
 
-如果需要更新框架使用
-~~~
-composer update topthink/framework
-~~~
+```
+php think apiadmin:install
+```
 
-## 文档
+> 第三步:完成数据迁移
 
-[完全开发手册](https://www.kancloud.cn/manual/thinkphp6_0/content)
+```
+php think migrate:run
+```
 
-## 参与开发
+> 第四步:构建后端路由
 
-请参阅 [ThinkPHP 核心框架包](https://github.com/top-think/framework)。
+```
+php think apiadmin:adminRouter
+```
 
-## 版权信息
+> 第五步:获取管理后台账号密码
 
-ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+```
+cat application/install/lock.ini
+```
 
-本项目包含的第三方源码和二进制文件之版权信息另行标注。
+## 灵 感
 
-版权所有Copyright © 2006-2020 by ThinkPHP (http://thinkphp.cn)
+首先自我介绍下吧,我是一个PHP程序员,目前就职于某上市集团。我第一份工作是做微信开发的,这也是我入行以来第一次做的商业上线项目,虽然我只是充当了其中一个不是太重要的角色,但是感谢它让我第一次接触了API,也让我第一次对于API产生了浓厚的兴趣。之后的一段时间内甚至疯狂的收集过各种免费的API接口!然而一直只是在用API,却没有为API贡献过些什么。
 
-All rights reserved。
+开源框架用了很多,开源代码看了很多,github、git@osc、Stack Overflow这些优秀的平台帮助了我很多,所以,我觉得是时候为开源做点什么。更是给开源项目PhalApi贡献过代码,也正是这一个契机使得我正式迈向开源社区。随着时间的推移,PhalApi的战绩赫赫,它的壮大更加坚定了Api的地位,既然未来的互联网世界中API占了很重要的地位,既然越来越多的人开始开发API,那么无状态的API如何去管理呢?因此**ApiAdmin**来了~
 
-ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+## 愿 景
 
-更多细节参阅 [LICENSE.txt](LICENSE.txt)
+> 希望有人用它,希望更多的人用它。
+> 希望它能帮助到你,希望它能帮助到更多的你。
+
+## 项目简介
+
+**系统需求**
+
+- PHP >= 7.2.5
+- MySQL >= 5.5.3
+- Redis
+
+**项目构成**
+
+- ThinkPHP v6.0.*
+- Vue 2.*
+- ...
+
+**功能简介**
+
+ 1. 接口文档自动生成
+ 2. 接口输入参数自动检查
+ 3. 接口输出参数数据类型自动规整
+ 4. 灵活的参数规则设定
+ 5. 支持三方Api无缝融合
+ 6. 本地二次开发友好
+ 7. ...
+ 
+ ```
+ ApiAdmin(PHP部分)
+ ├─ 系统维护
+ |  ├─ 菜单管理 - 编辑访客权限,处理菜单父子关系,被权限系统依赖(极为重要)
+ |  ├─ 用户管理 - 添加新用户,封号,删号以及给账号分配权限组
+ |  ├─ 权限管理 - 权限组管理,给权限组添加权限,将用户提出权限组
+ |  └─ 操作日志 - 记录管理员的操作,用于追责,回溯和备案
+ |  ...
+ ```
+
+**页面截图**
+
+![输入图片说明](https://gitee.com/uploads/images/2018/0224/095358_19cb42d0_110856.png "api.png")
+
+![输入图片说明](https://gitee.com/uploads/images/2018/0224/095410_55dc23e1_110856.png "app.png")
+
+![输入图片说明](https://gitee.com/uploads/images/2018/0224/095420_bddff990_110856.png "auth1.png")
+
+![输入图片说明](https://gitee.com/uploads/images/2018/0224/095427_fa86e42d_110856.png "auth2.png")
+
+![输入图片说明](https://gitee.com/uploads/images/2018/0224/095436_3600de17_110856.png "lock.png")
+
+![输入图片说明](https://gitee.com/uploads/images/2018/0224/095444_d2a88da0_110856.png "user.png")
+
+**项目特性**
+
+- 开放源码
+- 保持生机
+- 不断更新
+- 响应市场
+
+**开源,我们在路上!**
+
+## 鸣谢
+
+ApiAdmin走到今天,也正式迈入4.1时代了,我们怀着激动的心情迎来这次发布。在新版本发布之际,我们真诚的感谢从1.0到5.0陪我们一路走来的朋友们。感谢你们的支持和信任!当然也感谢#开源中国#给大陆本土开源提供这样一个优秀的平台。
+
+## 附:升级指南
+
+很抱歉的告诉大家,虽然我们尽可能的和往期版本进行了兼容,但是,由于整体架构变化很大,所以想要零成本升级有点困难。我们建议大家可以使用5.0做新接口,慢慢的将4.1版本的接口移植到5.0。
diff --git a/app/controller/admin/Menu.php b/app/controller/admin/Menu.php
new file mode 100644
index 0000000..8169a22
--- /dev/null
+++ b/app/controller/admin/Menu.php
@@ -0,0 +1,112 @@
+<?php
+declare (strict_types=1);
+/**
+ * 目录管理
+ * @since   2018-01-16
+ * @author  zhaoxiang <zhaoxiang051405@gmail.com>
+ */
+
+namespace app\controller\admin;
+
+use app\model\AdminMenu;
+use app\util\ReturnCode;
+use app\util\Tools;
+use think\Response;
+
+class Menu extends Base {
+
+    /**
+     * 获取菜单列表
+     * @return \think\Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function index(): Response {
+        $keywords = $this->request->get('keywords', '');
+        $obj = new AdminMenu();
+        if ($keywords) {
+            $obj = $obj->whereLike('title', "%{$keywords}%");
+        }
+        $obj = $obj->order('sort', 'ASC')->select();
+        $list = Tools::buildArrFromObj($obj);
+        if (!$keywords) {
+            $list = Tools::listToTree($list);
+        }
+
+        return $this->buildSuccess([
+            'list' => $list
+        ]);
+    }
+
+    /**
+     * 新增菜单
+     * @return \think\Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function add(): Response {
+        $postData = $this->request->post();
+        if ($postData['url']) {
+            $postData['url'] = 'admin/' . $postData['url'];
+        }
+        $res = AdminMenu::create($postData);
+        if ($res === false) {
+            return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
+        } else {
+            return $this->buildSuccess();
+        }
+    }
+
+    /**
+     * 菜单状态编辑
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function changeStatus(): Response {
+        $id = $this->request->get('id');
+        $status = $this->request->get('status');
+        $res = AdminMenu::update([
+            'id'   => $id,
+            'show' => $status
+        ]);
+        if ($res === false) {
+            return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
+        }
+
+        return $this->buildSuccess();
+    }
+
+    /**
+     * 编辑菜单
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function edit(): Response {
+        $postData = $this->request->post();
+        if ($postData['url']) {
+            $postData['url'] = 'admin/' . $postData['url'];
+        }
+        $res = AdminMenu::update($postData);
+        if ($res === false) {
+            return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
+        }
+
+        return $this->buildSuccess();
+    }
+
+    /**
+     * 删除菜单
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function del(): Response {
+        $id = $this->request->get('id');
+        if (!$id) {
+            return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
+        }
+        (new AdminMenu())->whereIn('id', $id)->delete();
+
+        return $this->buildSuccess();
+    }
+}
diff --git a/app/controller/admin/ThirdLogin.php b/app/controller/admin/ThirdLogin.php
new file mode 100644
index 0000000..283a713
--- /dev/null
+++ b/app/controller/admin/ThirdLogin.php
@@ -0,0 +1,350 @@
+<?php
+declare (strict_types=1);
+/**
+ * 三方一键登录平台
+ * @since   2018-03-28
+ * @author  zhaoxiang <zhaoxiang051405@gmail.com>
+ */
+
+namespace app\controller\admin;
+
+use app\model\AdminAuthGroupAccess;
+use app\model\AdminUser;
+use app\util\ReturnCode;
+use app\util\Strs;
+use app\util\Tools;
+use Endroid\QrCode\ErrorCorrectionLevel;
+use Endroid\QrCode\QrCode;
+use think\facade\Cache;
+use think\facade\Env;
+use think\Response;
+
+class ThirdLogin extends Base {
+
+    /**
+     * QQ一键登录配置
+     * @var array
+     */
+    private $qqConfig = [
+        'appId'       => '',
+        'appSecret'   => '',
+        'redirectUri' => 'https://admin.apiadmin.org/#/login/qq'
+    ];
+
+    /**
+     * 微信认证服务号一键登录配置
+     * @var array
+     */
+    private $wxConfig = [
+        'appId'     => '',
+        'appSecret' => ''
+    ];
+
+    /**
+     * 微信开放平台一键登录配置
+     * @var array
+     */
+    private $wxOpenConfig = [
+        'appId'       => '',
+        'appSecret'   => '',
+        'redirectUri' => 'https://admin.apiadmin.org/#/login/wx'
+    ];
+
+    /**
+     * 使用微信开放平台登录
+     * @return Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function wx(): Response {
+        $state = $this->request->get('state', '');
+        $code = $this->request->get('code', '');
+
+        //验证合法性
+        $cacheData = Cache::has($state);
+        if (!$cacheData) {
+            return $this->buildFailed(ReturnCode::SESSION_TIMEOUT, 'state已过期');
+        } else {
+            cache($state, null);
+        }
+
+        //获取AccessToken
+        $getAccessTokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' .
+            $this->wxOpenConfig['appId'] . '&secret=' . $this->wxOpenConfig['appSecret'] . '&code=' . $code .
+            '&grant_type=authorization_code';
+
+        $tokenArr = file_get_contents($getAccessTokenUrl);
+        $accessTokenArr = json_decode($tokenArr, true);
+
+        //获取openId
+        $getUserIdUrl = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $accessTokenArr['access_token'] . '&openid=' . $accessTokenArr['openid'];
+        $userIdArr = file_get_contents($getUserIdUrl);
+        $userIdArr = json_decode($userIdArr, true);
+
+        return $this->doLogin($userIdArr['openid'], [
+            'nickname' => $userIdArr['nickname'],
+            'head_img' => $userIdArr['headimgurl']
+        ]);
+    }
+
+    /**
+     * 获取授权登录的二维码
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function getQr(): Response {
+        $state = uniqid();
+        $query = [
+            'appid'         => $this->wxConfig['appId'],
+            'redirect_uri'  => 'https://api.apiadmin.org/admin/ThirdLogin/loginByWx',
+            'response_type' => 'code',
+            'scope'         => 'snsapi_userinfo',
+            'state'         => $state
+        ];
+        $authUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($query) . '#wechat_redirect';
+
+        $qrCode = new QrCode($authUrl);
+        $qrCode->setSize(300);
+        $qrCode->setWriterByName('png');
+        $qrCode->setMargin(10);
+        $qrCode->setEncoding('UTF-8');
+        $qrCode->setErrorCorrectionLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::HIGH));
+        $qrCode->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0]);
+        $qrCode->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]);
+        $qrCode->setRoundBlockSize(true);
+        $qrCode->setValidateResult(false);
+        $qrCode->writeFile(Env::get('root_path') . 'public/qr/' . $state . '.png');
+
+        cache($state, 1, 300);
+
+        return $this->buildSuccess([
+            'qrUrl' => 'https://api.apiadmin.org/qr/' . $state . '.png',
+            'state' => $state
+        ]);
+
+    }
+
+    /**
+     * 接受微信回调,处理用户登录
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function loginByWx(): Response {
+        $code = $this->request->get('code');
+        $state = $this->request->get('state');
+
+        $auth = cache($state);
+        if (!$auth) {
+            return view('wiki@index/login_res', [
+                'info' => '当前二维码已失效',
+                'code' => ReturnCode::RECORD_NOT_FOUND
+            ]);
+        }
+
+        $query = [
+            'appid'      => $this->wxConfig['appId'],
+            'secret'     => $this->wxConfig['appSecret'],
+            'grant_type' => 'authorization_code',
+            'code'       => $code
+        ];
+        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($query);
+        $accessToken = json_decode(file_get_contents($url), true);
+
+        $getUserInfoQuery = [
+            'access_token' => $accessToken['access_token'],
+            'openid'       => $accessToken['openid'],
+            'lang'         => 'zh_CN'
+        ];
+        $getUserInfoUrl = 'https://api.weixin.qq.com/sns/userinfo?' . http_build_query($getUserInfoQuery);
+
+        $userInfoArr = file_get_contents($getUserInfoUrl);
+        $userInfoArr = json_decode($userInfoArr, true);
+
+        if ($userInfoArr) {
+            cache($state, [
+                'nickname' => $userInfoArr['nickname'],
+                'head_img' => $userInfoArr['headimgurl'],
+                'openid'   => $accessToken['openid']
+            ], 300);
+
+            return view('wiki@index/login_res', [
+                'info' => '登录成功',
+                'code' => ReturnCode::SUCCESS
+            ]);
+        } else {
+            return view('wiki@index/login_res', [
+                'info' => '操作失败',
+                'code' => ReturnCode::DB_SAVE_ERROR
+            ]);
+        }
+    }
+
+    /**
+     * 处理微信用户登录
+     * @return Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function checkWxLogin(): Response {
+        $state = $this->request->get('state');
+        $userInfo = cache($state);
+
+        if (is_numeric($userInfo)) {
+            return $this->buildFailed(666, '等待扫码');
+        } else {
+            @unlink(Env::get('root_path') . 'public/qr/' . $state . '.png');
+            if (is_array($userInfo)) {
+                cache($state, null);
+
+                return $this->doLogin($userInfo['openid'], [
+                    'nickname' => $userInfo['nickname'],
+                    'head_img' => $userInfo['head_img']
+                ]);
+            } else {
+                return $this->buildFailed(ReturnCode::INVALID, '登录状态已失效,请重新登录');
+            }
+        }
+    }
+
+    /**
+     * 获取qq登录必要参数
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function getWxCode(): Response {
+        $state = md5(uniqid() . time());
+        cache($state, $state, 300);
+
+        return $this->buildSuccess([
+            'appId'       => $this->wxOpenConfig['appId'],
+            'redirectUri' => urlencode($this->wxOpenConfig['redirectUri']),
+            'state'       => $state
+        ]);
+    }
+
+    /**
+     * 获取qq登录必要参数
+     * @return Response
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function getQQCode(): Response {
+        $state = md5(uniqid() . time());
+        cache($state, $state, 300);
+
+        return $this->buildSuccess([
+            'appId'       => $this->qqConfig['appId'],
+            'redirectUri' => urlencode($this->qqConfig['redirectUri']),
+            'state'       => $state
+        ]);
+    }
+
+    /**
+     * 使用QQ登录
+     * @return Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    public function loginByQQ(): Response {
+        $state = $this->request->get('state', '');
+        $code = $this->request->get('code', '');
+
+        //验证合法性
+        $cacheData = Cache::has($state);
+        if (!$cacheData) {
+            return $this->buildFailed(ReturnCode::SESSION_TIMEOUT, 'state已过期');
+        } else {
+            cache($state, null);
+        }
+
+        //获取AccessToken
+        $getAccessTokenUrl = 'https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=' .
+            $this->qqConfig['appId'] . '&client_secret=' . $this->qqConfig['appSecret'] . '&code=' . $code .
+            '&redirect_uri=' . urlencode($this->qqConfig['redirectUri']);
+
+        $tokenArr = file_get_contents($getAccessTokenUrl);
+        parse_str($tokenArr, $accessTokenArr);
+
+        //获取openId
+        $getUserIdUrl = 'https://graph.qq.com/oauth2.0/me?access_token=' . $accessTokenArr['access_token'];
+        $userIdArr = file_get_contents($getUserIdUrl);
+        $userIdArr = str_replace('callback( ', '', $userIdArr);
+        $userIdArr = str_replace(' );', '', $userIdArr);
+        $userIdArr = json_decode($userIdArr, true);
+
+        $getUserInfoUrl = 'https://graph.qq.com/user/get_user_info?access_token=' . $accessTokenArr['access_token'] . '&oauth_consumer_key=' .
+            $this->qqConfig['appId'] . '&openid=' . $userIdArr['openid'];
+        $userInfoArr = file_get_contents($getUserInfoUrl);
+        $userInfoArr = json_decode($userInfoArr, true);
+
+        return $this->doLogin($userIdArr['openid'], [
+            'nickname' => $userInfoArr['nickname'],
+            'head_img' => $userInfoArr['figureurl_qq_2']
+        ]);
+    }
+
+    /**
+     * 统一处理用户登录
+     * @param $openid
+     * @param $userDetail
+     * @return Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author zhaoxiang <zhaoxiang051405@gmail.com>
+     */
+    private function doLogin($openid, $userDetail): Response {
+        $userInfo = AdminUser::get(['openid' => $openid]);
+        if (empty($userInfo)) {
+            $userInfo = AdminUser::create([
+                'nickname'    => $userDetail['nickname'],
+                'username'    => 'ApiAdmin_qq_' . Strs::randString(8),
+                'openid'      => $openid,
+                'create_ip'   => request()->ip(1),
+                'status'      => 1,
+                'create_time' => time(),
+                'password'    => Tools::userMd5('ApiAdmin')
+            ]);
+            $userDataArr = [
+                'login_times'     => 1,
+                'uid'             => $userInfo->id,
+                'last_login_ip'   => $this->request->ip(1),
+                'last_login_time' => time(),
+                'head_img'        => $userDetail['head_img']
+            ];
+            $userInfo->userData()->save($userDataArr);
+            $userInfo['userData'] = $userDataArr;
+
+            AdminAuthGroupAccess::create([
+                'uid'      => $userInfo->id,
+                'group_id' => 1
+            ]);
+        } else {
+            if ($userInfo['status']) {
+                //更新用户数据
+                $userInfo->userData->login_times++;
+                $userInfo->userData->last_login_ip = $this->request->ip(1);
+                $userInfo->userData->last_login_time = time();
+                $userInfo->userData->save();
+            } else {
+                return $this->buildFailed(ReturnCode::LOGIN_ERROR, '用户已被封禁,请联系管理员');
+            }
+        }
+
+        $userInfo['access'] = (new Login())->getAccess($userInfo['id']);
+        $userInfo['menu'] = (new Login())->getAccessMenu($userInfo['id']);
+
+        $apiAuth = md5(uniqid() . time());
+        cache('Login:' . $apiAuth, json_encode($userInfo), config('apiadmin.ONLINE_TIME'));
+        cache('Login:' . $userInfo['id'], $apiAuth, config('apiadmin.ONLINE_TIME'));
+
+        $userInfo['apiAuth'] = $apiAuth;
+
+        return $this->buildSuccess($userInfo->toArray(), '登录成功');
+    }
+}
diff --git a/composer.json b/composer.json
index 6cbb7bd..fa703fb 100644
--- a/composer.json
+++ b/composer.json
@@ -20,9 +20,10 @@
         }
     ],
     "require": {
-        "php": ">=7.1.0",
-        "topthink/framework": "^6.0.0",
-        "topthink/think-orm": "^2.0"
+        "php": ">=7.2.5",
+        "topthink/framework": "^6.0",
+        "topthink/think-orm": "^2.0",
+        "endroid/qrcode": "^3.9"
     },
     "require-dev": {
         "symfony/var-dumper": "^4.2",
diff --git a/composer.lock b/composer.lock
index 44a1c0f..75be5a2 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,240 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "74a749574e0f40df27b0061a71e2b878",
+    "content-hash": "a35b018c21863e25289a0e88fe7a3c19",
     "packages": [
+        {
+            "name": "bacon/bacon-qr-code",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Bacon/BaconQrCode.git",
+                "reference": "add6d9ff97336b62f95a3b94f75cea4e085465b2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/add6d9ff97336b62f95a3b94f75cea4e085465b2",
+                "reference": "add6d9ff97336b62f95a3b94f75cea4e085465b2",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "dasprid/enum": "^1.0",
+                "ext-iconv": "*",
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phly/keep-a-changelog": "^1.4",
+                "phpunit/phpunit": "^7 | ^8 | ^9",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "suggest": {
+                "ext-imagick": "to generate QR code images"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "BaconQrCode\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Scholzen 'DASPRiD'",
+                    "email": "mail@dasprids.de",
+                    "homepage": "https://dasprids.de/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "BaconQrCode is a QR code generator for PHP.",
+            "homepage": "https://github.com/Bacon/BaconQrCode",
+            "time": "2020-07-30T16:40:58+00:00"
+        },
+        {
+            "name": "dasprid/enum",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/DASPRiD/Enum.git",
+                "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
+                "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7 | ^8 | ^9",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DASPRiD\\Enum\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Scholzen 'DASPRiD'",
+                    "email": "mail@dasprids.de",
+                    "homepage": "https://dasprids.de/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP 7.1 enum implementation",
+            "keywords": [
+                "enum",
+                "map"
+            ],
+            "time": "2020-10-02T16:03:48+00:00"
+        },
+        {
+            "name": "endroid/qrcode",
+            "version": "3.9.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/endroid/qr-code.git",
+                "reference": "58d5872ca46b99b5c2e72cd2c8dea09ce2988156"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/endroid/qr-code/zipball/58d5872ca46b99b5c2e72cd2c8dea09ce2988156",
+                "reference": "58d5872ca46b99b5c2e72cd2c8dea09ce2988156",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "bacon/bacon-qr-code": "^2.0",
+                "ext-gd": "*",
+                "khanamiryan/qrcode-detector-decoder": "^1.0.2",
+                "myclabs/php-enum": "^1.5",
+                "php": ">=7.2",
+                "symfony/options-resolver": "^3.4||^4.4||^5.0",
+                "symfony/property-access": "^3.4||^4.4||^5.0"
+            },
+            "require-dev": {
+                "endroid/quality": "dev-master",
+                "setasign/fpdf": "^1.8"
+            },
+            "suggest": {
+                "roave/security-advisories": "Avoids installation of package versions with vulnerabilities",
+                "setasign/fpdf": "Required to use the FPDF writer.",
+                "symfony/security-checker": "Checks your composer.lock for vulnerabilities"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Endroid\\QrCode\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jeroen van den Enden",
+                    "email": "info@endroid.nl"
+                }
+            ],
+            "description": "Endroid QR Code",
+            "homepage": "https://github.com/endroid/qr-code",
+            "keywords": [
+                "bundle",
+                "code",
+                "endroid",
+                "php",
+                "qr",
+                "qrcode"
+            ],
+            "abandoned": "endroid/qr-code",
+            "time": "2020-10-07T09:42:59+00:00"
+        },
+        {
+            "name": "khanamiryan/qrcode-detector-decoder",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git",
+                "reference": "89b57f2d9939dd57394b83f6ccbd3e1a74659e34"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/89b57f2d9939dd57394b83f6ccbd3e1a74659e34",
+                "reference": "89b57f2d9939dd57394b83f6ccbd3e1a74659e34",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": "^5.6|^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Zxing\\": "lib/"
+                },
+                "files": [
+                    "lib/Common/customFunctions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ashot Khanamiryan",
+                    "email": "a.khanamiryan@gmail.com",
+                    "homepage": "https://github.com/khanamiryan",
+                    "role": "Developer"
+                }
+            ],
+            "description": "QR code decoder / reader",
+            "homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder/",
+            "keywords": [
+                "barcode",
+                "qr",
+                "zxing"
+            ],
+            "time": "2020-04-19T16:18:51+00:00"
+        },
         {
             "name": "league/flysystem",
             "version": "1.1.3",
@@ -197,6 +429,58 @@
             "description": "Mime-type detection for Flysystem",
             "time": "2020-09-21T18:10:53+00:00"
         },
+        {
+            "name": "myclabs/php-enum",
+            "version": "1.7.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/php-enum.git",
+                "reference": "5f36467c7a87e20fbdc51e524fd8f9d1de80187c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/php-enum/zipball/5f36467c7a87e20fbdc51e524fd8f9d1de80187c",
+                "reference": "5f36467c7a87e20fbdc51e524fd8f9d1de80187c",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-json": "*",
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7",
+                "squizlabs/php_codesniffer": "1.*",
+                "vimeo/psalm": "^3.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "MyCLabs\\Enum\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP Enum contributors",
+                    "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+                }
+            ],
+            "description": "PHP Enum implementation",
+            "homepage": "http://github.com/myclabs/php-enum",
+            "keywords": [
+                "enum"
+            ],
+            "time": "2020-02-14T08:15:52+00:00"
+        },
         {
             "name": "opis/closure",
             "version": "3.5.7",
@@ -478,6 +762,710 @@
             ],
             "time": "2017-10-23T01:57:42+00:00"
         },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "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": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "time": "2020-09-07T11:33:47+00:00"
+        },
+        {
+            "name": "symfony/options-resolver",
+            "version": "v5.1.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/options-resolver.git",
+                "reference": "4c7e155bf7d93ea4ba3824d5a14476694a5278dd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4c7e155bf7d93ea4ba3824d5a14476694a5278dd",
+                "reference": "4c7e155bf7d93ea4ba3824d5a14476694a5278dd",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\OptionsResolver\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony OptionsResolver Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "config",
+                "configuration",
+                "options"
+            ],
+            "time": "2020-09-27T03:44:28+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
+                "reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-grapheme",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+                "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5",
+                "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "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 intl's grapheme_* functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "grapheme",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
+                "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "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 intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
+                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "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"
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+                "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.0.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "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"
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/property-access",
+            "version": "v5.1.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/property-access.git",
+                "reference": "4c43f7ff784e1e3ee1c96e15f76b342af6617b39"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/property-access/zipball/4c43f7ff784e1e3ee1c96e15f76b342af6617b39",
+                "reference": "4c43f7ff784e1e3ee1c96e15f76b342af6617b39",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/property-info": "^5.1.1"
+            },
+            "require-dev": {
+                "symfony/cache": "^4.4|^5.0"
+            },
+            "suggest": {
+                "psr/cache-implementation": "To cache access methods."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\PropertyAccess\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony PropertyAccess Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "access",
+                "array",
+                "extraction",
+                "index",
+                "injection",
+                "object",
+                "property",
+                "property path",
+                "reflection"
+            ],
+            "time": "2020-09-02T16:23:27+00:00"
+        },
+        {
+            "name": "symfony/property-info",
+            "version": "v5.1.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/property-info.git",
+                "reference": "22518930091e0bdb249694efc509e3697f7e325e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/property-info/zipball/22518930091e0bdb249694efc509e3697f7e325e",
+                "reference": "22518930091e0bdb249694efc509e3697f7e325e",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-php80": "^1.15",
+                "symfony/string": "^5.1"
+            },
+            "conflict": {
+                "phpdocumentor/reflection-docblock": "<3.2.2",
+                "phpdocumentor/type-resolver": "<0.3.0",
+                "symfony/dependency-injection": "<4.4"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.7",
+                "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+                "symfony/cache": "^4.4|^5.0",
+                "symfony/dependency-injection": "^4.4|^5.0",
+                "symfony/serializer": "^4.4|^5.0"
+            },
+            "suggest": {
+                "phpdocumentor/reflection-docblock": "To use the PHPDoc",
+                "psr/cache-implementation": "To cache results",
+                "symfony/doctrine-bridge": "To use Doctrine metadata",
+                "symfony/serializer": "To use Serializer metadata"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\PropertyInfo\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kévin Dunglas",
+                    "email": "dunglas@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Property Info Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "doctrine",
+                "phpdoc",
+                "property",
+                "symfony",
+                "type",
+                "validator"
+            ],
+            "time": "2020-09-07T05:10:28+00:00"
+        },
+        {
+            "name": "symfony/string",
+            "version": "v5.1.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/string.git",
+                "reference": "4a9afe9d07bac506f75bcee8ed3ce76da5a9343e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/string/zipball/4a9afe9d07bac506f75bcee8ed3ce76da5a9343e",
+                "reference": "4a9afe9d07bac506f75bcee8ed3ce76da5a9343e",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-intl-grapheme": "~1.0",
+                "symfony/polyfill-intl-normalizer": "~1.0",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php80": "~1.15"
+            },
+            "require-dev": {
+                "symfony/error-handler": "^4.4|^5.0",
+                "symfony/http-client": "^4.4|^5.0",
+                "symfony/translation-contracts": "^1.1|^2",
+                "symfony/var-exporter": "^4.4|^5.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\String\\": ""
+                },
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "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": "Symfony String component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "grapheme",
+                "i18n",
+                "string",
+                "unicode",
+                "utf-8",
+                "utf8"
+            ],
+            "time": "2020-09-15T12:23:47+00:00"
+        },
         {
             "name": "topthink/framework",
             "version": "v6.0.3",
@@ -644,75 +1632,6 @@
         }
     ],
     "packages-dev": [
-        {
-            "name": "symfony/polyfill-mbstring",
-            "version": "v1.18.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
-                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
-                "shasum": "",
-                "mirrors": [
-                    {
-                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
-                        "preferred": true
-                    }
-                ]
-            },
-            "require": {
-                "php": ">=5.3.3"
-            },
-            "suggest": {
-                "ext-mbstring": "For best performance"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.18-dev"
-                },
-                "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Polyfill\\Mbstring\\": ""
-                },
-                "files": [
-                    "bootstrap.php"
-                ]
-            },
-            "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"
-            ],
-            "time": "2020-07-14T12:35:20+00:00"
-        },
         {
             "name": "symfony/polyfill-php72",
             "version": "v1.18.1",
@@ -778,78 +1697,6 @@
             ],
             "time": "2020-07-14T12:35:20+00:00"
         },
-        {
-            "name": "symfony/polyfill-php80",
-            "version": "v1.18.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
-                "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
-                "shasum": "",
-                "mirrors": [
-                    {
-                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
-                        "preferred": true
-                    }
-                ]
-            },
-            "require": {
-                "php": ">=7.0.8"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.18-dev"
-                },
-                "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Polyfill\\Php80\\": ""
-                },
-                "files": [
-                    "bootstrap.php"
-                ],
-                "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"
-            ],
-            "time": "2020-07-14T12:35:20+00:00"
-        },
         {
             "name": "symfony/var-dumper",
             "version": "v4.4.13",