commit ae67f07f3a24c0cb525eedd4efbda737eb4e1cc4 Author: ahbmz Date: Tue Mar 29 19:18:18 2022 +0800 初始化版本 diff --git a/.env b/.env new file mode 100644 index 0000000..aa749e3 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +APP_DEBUG = false [APP] DEFAULT_TIMEZONE = Asia/Shanghai [DATABASE] TYPE = mysql HOSTNAME = 122.114.59.173 DATABASE = bmz_base USERNAME = bmz_base PASSWORD = C2dA3FPLZxhDjKnr HOSTPORT = 3306 CHARSET = utf8mb4 DEBUG = true [LANG] default_lang = zh-cn \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3174301 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.idea +/.vscode +*.log +/runtime +/verdor +/vendor/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..36f7b6f --- /dev/null +++ b/.travis.yml @@ -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 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..574a39c --- /dev/null +++ b/LICENSE.txt @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7661c30 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +ThinkPHP 6.0 +=============== + +> 运行环境要求PHP7.1+。 + +## 主要新特性 + +* 采用`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-2020 by ThinkPHP (http://thinkphp.cn) + +All rights reserved。 + +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 + +更多细节参阅 [LICENSE.txt](LICENSE.txt) diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/app/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/app/AppService.php b/app/AppService.php new file mode 100644 index 0000000..96556e8 --- /dev/null +++ b/app/AppService.php @@ -0,0 +1,22 @@ +app = $app; + $this->request = $this->app->request; + + // 控制器初始化 + $this->initialize(); + } + + // 初始化 + protected function initialize() + {} + + /** + * 验证数据 + * @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); + } + +} diff --git a/app/ExceptionHandle.php b/app/ExceptionHandle.php new file mode 100644 index 0000000..453d126 --- /dev/null +++ b/app/ExceptionHandle.php @@ -0,0 +1,58 @@ + 1, + 'limit' => 10, +]; + diff --git a/app/api/config/jwtauth.php b/app/api/config/jwtauth.php new file mode 100644 index 0000000..a223219 --- /dev/null +++ b/app/api/config/jwtauth.php @@ -0,0 +1,23 @@ + [ + 'host' => 'base.ahbmz.com', + 'expire_time' => '+1200 minute', //分钟 DateTimeImmutable + 'expire_at' => 72000, //秒 //对应expire_time + 'token_key' => 'K4Eya8bEbxmjzxcplUo8u3d', + 'jti_nonce' => 'lwEinge4Ey8ExjxpU83klKlfeiao8L', //jwt jti + 'expire_time_refresh' => 3600 * 24 * 7, //7天过期 + ], + //不需要jwt的控制器、方法直接返回 + 'filter_router' => [ + 'api/login/login', + 'api/login/refreshtoken', + 'api/login/captcha', + 'api/UploadLog/add', + ] +]; diff --git a/app/api/controller/Ad.php b/app/api/controller/Ad.php new file mode 100644 index 0000000..243421f --- /dev/null +++ b/app/api/controller/Ad.php @@ -0,0 +1,205 @@ +model = new \app\api\model\Ad(); + } + + /** + * 显示资源列表 + * + * @return \think\Response + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 +// validate(\app\api\validate\Block::class)->scene('lists')->check($_get); + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + /** + * 获取广告位类型 + * + * @return \think\Response + */ + public function getAdType(){ + //获取栏目类型 + $_ad_type = config('dictionary.sqlfields.ad_type'); + //返回数据 + return send_http_status($_ad_type); + } + + /** + * 添加数据 + * @return \think\Response + * @Log [sys_name] 在 [sys_time] 创建了广告位:[name] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Ad::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + + return send_http_status($insertId, $insertId ? 201 : 202); + } + + + /** + * 读取单条数据 + * @param int $id + * @return \think\Response + */ + public function read($id = '') + { + //验证参数 + validate(\app\api\validate\Block::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * 保存更新的资源 + * @param \think\Request $request + * @param int $id + * @return \think\Response + * @Log [sys_name]在[sys_time]修改了广告位[id]的数据 + */ + public function edit($id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = $id; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Ad::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * 删除指定资源 + * @Log [sys_name] 在 [sys_time] 删除了广告位id:[id] + * @param int $id + * @return \think\Response + */ + public function delete() + { + //接收参数 + $ids = $this->request->get('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,10'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * 显示广告列表 + * + * @return \think\Response + */ + public function data() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 +// validate(\app\api\validate\Block::class)->scene('lists')->check($_get); + + //数据处理 + $_data = $this->model->AdData(); + + //返回数据 + return send_http_status($_data); + } + + public function dataAdd(){ + $this->model = new \app\api\model\AdData(); + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\AdData::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + + return send_http_status($insertId, $insertId ? 201 : 202); + } + + public function dataEdit($id = 0){ + $this->model = new \app\api\model\AdData(); + //接收参数 + $_post = $this->request->post(); + $_post['id'] = $id; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\AdData::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + public function dataRead($id=0){ + $this->model = new \app\api\model\AdData(); + //验证参数 + validate(\app\api\validate\AdData::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * 删除指定资源 + * @Log [sys_name] 在 [sys_time] 删除了广告id:[id] + * @param int $id + * @return \think\Response + */ + public function dataDelete() + { + $this->model = new \app\api\model\AdData(); + //接收参数 + $ids = $this->request->get('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,10'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + +} diff --git a/app/api/controller/Block.php b/app/api/controller/Block.php new file mode 100644 index 0000000..97f20e1 --- /dev/null +++ b/app/api/controller/Block.php @@ -0,0 +1,118 @@ +model = new \app\api\model\Block(); + } + + /** + * 显示资源列表 + * + * @return \think\Response + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 +// validate(\app\api\validate\Block::class)->scene('lists')->check($_get); + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + /** + * 添加数据 + * @return \think\Response + * @Log [sys_name] 在 [sys_time] 创建了管理员:[ident] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Block::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + + return send_http_status($insertId, $insertId ? 201 : 202); + } + + + /** + * 读取单条数据 + * @param int $id + * @return \think\Response + */ + public function read($id = '') + { + //验证参数 + validate(\app\api\validate\Block::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * 保存更新的资源 + * @param \think\Request $request + * @param int $id + * @return \think\Response + * @Log [sys_name]在[sys_time]修改了碎片标志[ident]的数据 + */ + public function edit($id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = $id; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Block::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * 删除指定资源 + * @Log [sys_name] 在 [sys_time] 删除了碎片id:[id] + * @param int $id + * @return \think\Response + */ + public function delete() + { + //接收参数 + $ids = $this->request->post('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,10'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + +} diff --git a/app/api/controller/Column.php b/app/api/controller/Column.php new file mode 100644 index 0000000..3ccacfa --- /dev/null +++ b/app/api/controller/Column.php @@ -0,0 +1,135 @@ +model = new \app\api\model\Column(); + } + + /** + * 显示资源列表 + * + * @return \think\Response + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data, 200); + } + + /** + * 添加数据 + * @return \think\Response + * @Log [sys_name] 在 [sys_time] 创建了栏目:[name] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Manager::class)->check($_post); + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + + /** + * 读取单条数据 + * @param int $id + * @return \think\Response + */ + public function read($id = '') + { + //验证参数 + validate(\app\api\validate\Manager::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * 保存更新的资源 + * @param \think\Request $request + * @param int $id + * @return \think\Response + * @Log [sys_name]在[sys_time]修改了管理员账号[name]的数据 + */ + public function edit($id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = $id; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Manager::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * 删除指定资源 + * @Log [sys_name] 在 [sys_time] 删除了栏目名称:[name] + * @param int $id + * @return \think\Response + */ + public function delete() + { + //接收参数 + $ids = $this->request->post('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,100'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * 修改数据状态 + * @param int $id + * @return \think\Response + * @Log [sys_name] 在 [sys_time] 设置ID为[id]管理员账号为:[dictionary_status] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } +} diff --git a/app/api/controller/Common.php b/app/api/controller/Common.php new file mode 100644 index 0000000..546208a --- /dev/null +++ b/app/api/controller/Common.php @@ -0,0 +1,117 @@ +action_url = $this->getBaseActionUrl(); + + //是否跳过token、权限验证 + if (!$this->filterJwtAuth()) { + /** + * JWT Token检测 + */ + if ((!defined('UID') || !defined('SITE_ID'))) { + return send_http_status('', 40512); + } + + /** + * 定义当前类的实例化[操作日志类使用] + */ + defined('CURR_THIS') ?: define('CURR_THIS', get_called_class()); + + /** + * 检测用户权限 + */ + env('app_debug') ?: $this->checkPermissAuth(); // $this->checkPermissAuth(); + } + /** + * 自定义全局验证器 + */ + $this->uniqueSite(); + } + + /** + * + * 自定义全局验证器,检测同站点下数据是否重复 + */ + private function uniqueSite() + { + Validate::maker(function ($validate) { + $validate->extend('uniqueSite', 'extra_unique_validate'); + }); + } + + /** + * 检测用户权限 + */ + private function checkPermissAuth() + { + $url = str_replace(['//', '\\', '/'], '', $this->request->root()); + $url .= '/' . $this->request->controller(); + $url .= '/' . $this->request->action(); + if (!(PermissAuth::getInstance())->check($url, UID)) { + return send_http_status('', 403); + } + } + + /** + * jwt 过滤请求地址,如果返回 true 说明不需要验证 + * @return bool + */ + private function filterJwtAuth() + { + $filter_router = config('jwtauth.filter_router'); + if (empty($filter_router)) { + return false; + } + if (in_array($this->action_url, array_map('strtolower', $filter_router))) { + return true; + } + return false; + } + + /** + * 获取请求路径 模块名/控制器名/方法名 + * @return string + */ + private function getBaseActionUrl() + { + $url = str_replace(['//', '\\', '/'], '', $this->request->root()); + $url .= '/' . $this->request->controller(); + $url .= '/' . $this->request->action(); + return strtolower($url); + } + +} diff --git a/app/api/controller/Config.php b/app/api/controller/Config.php new file mode 100644 index 0000000..2a166ad --- /dev/null +++ b/app/api/controller/Config.php @@ -0,0 +1,90 @@ +model = new \app\api\model\Config(); + } + + /** + * @OA\Get ( + * path="config/index",tags={"系统配置"},summary="获取系统配置",description="获取系统配置", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="name",in="query",description="配置名称",@OA\Schema (type="string")), + * @OA\Parameter(name="type",in="query",description="配置标识",@OA\Schema (type="string")), + * @OA\Parameter(name="value",in="query",description="配置值",@OA\Schema (type="string")), + * @OA\Response( + * response=200, + * description="返回所有配置信息", + * ), + * ) + */ + public function index() + { + //接受参数 + $_get = $this->request->param(); + + //验证参数 + validate(\app\api\validate\Config::class)->scene('lists')->check($_get); + + //指定搜索参数 + $this->model->search_arr = ['name', 'type', 'value']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Post (path="config/save",tags={"系统配置"},summary="系统配置",description="系统配置", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * @OA\Property(property="name",type="string",description="此那么可自行定义 比如 seo_title"), + * @OA\Property(property="value",type="string",description="此那么可自行定义 比如 合肥网站优化"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录总数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 更新了配置信息 + */ + public function save() + { + //接收参数 + $_post = $this->request->post(); + + //处理并返回参数 + $insertId = $this->model->edit($_post); + return send_http_status($insertId, $insertId ? 209 : 210); + } + +} diff --git a/app/api/controller/Demo.php b/app/api/controller/Demo.php new file mode 100644 index 0000000..142f227 --- /dev/null +++ b/app/api/controller/Demo.php @@ -0,0 +1,242 @@ +model = new \app\api\model\Demo(); + } + + /** + * @OA\Get ( + * path="demo/index",tags={"测试案例"},summary="获取DEMO列表",description="根据相关参数获取DEMO列表", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="username",in="query",description="用户名称",@OA\Schema (type="string")), + * @OA\Parameter (name="sex",in="query",description="用户年龄",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="page",in="query",description="当前页码",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="limit",in="query",description="分页数量",@OA\Schema (type="integer",format="int15")), + * @OA\Response( + * response=200, + * description="返回DEMo用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/DemoMsgExport"), + * ), + * ) + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 + validate(\app\api\validate\Demo::class)->scene('lists')->check($_get); + + //指定搜索参数 + $this->model->search_arr = ['username', 'phone:OR', 'sex:OR', 'truename']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Get ( + * path="demo/read/{id}",tags={"测试案例"},summary="获取DEMO详情",description="根据ID查询DEMO信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="id",in="path",required=true,description="ID",@OA\Schema (type="integer",format="int32")), + * @OA\Response( + * response=200, + * description="返回DEMO单条用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/DemoMsgExport"), + * ), + * ) + */ + public function read(int $id = 0) + { + //验证参数 + validate(\app\api\validate\Demo::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * @OA\Post (path="demo/add",tags={"测试案例"},summary="添加DEMO数据",description="添加后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了测试:[username] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Demo::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @OA\Post (path="demo/edit/id/{id}",tags={"测试案例"},summary="修改DEMO数据",description="修改后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time]修改了测试账号[username]的数据 + */ + public function edit(int $id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = empty($_post['id']) ? $id : 0; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Demo::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * @OA\Post ( + * path="demo/delete",tags={"测试案例"},summary="删除DEMO数据",description="根据ID删除DEMO信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了测试账号:[username] + */ + public function delete() + { + //接收参数 + $ids = $this->request->post('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,1000'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * @OA\Post ( + * path="demo/status",tags={"测试案例"},summary="更新DEMO状态",description="根据ID及status更新DEMO状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 设置ID为[id]测试账号为:[dictionary_status] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } + +} diff --git a/app/api/controller/Dictionary.php b/app/api/controller/Dictionary.php new file mode 100644 index 0000000..7e0baaf --- /dev/null +++ b/app/api/controller/Dictionary.php @@ -0,0 +1,203 @@ +model = new \app\api\model\Dictionary(); + } + + + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 +// validate(\app\api\validate\Dictionary::class)->scene('lists')->check($_get); + + //指定搜索参数 + $this->model->search_arr = ['dict_name','dict_value:OR']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + public function read(int $id = 0) + { + //验证参数 + validate(\app\api\validate\Dictionary::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * @OA\Post (path="demo/add",tags={"测试案例"},summary="添加DEMO数据",description="添加后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了字典类型:[dict_symbol]:[dict_name] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Dictionary::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @OA\Post (path="demo/edit/id/{id}",tags={"测试案例"},summary="修改DEMO数据",description="修改后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time]修改了字典[dict_symbol]的数据 + */ + public function edit(int $id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = empty($_post['id']) ? $id : 0; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Dictionary::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * @OA\Post ( + * path="demo/delete",tags={"测试案例"},summary="删除DEMO数据",description="根据ID删除DEMO信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了字典:[dict_symbol]:[dict_name] + */ + public function delete() + { + //接收参数 + $ids = $this->request->param('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,1000'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * @OA\Post ( + * path="demo/status",tags={"测试案例"},summary="更新DEMO状态",description="根据ID及status更新DEMO状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 设置ID为[id]测试账号为:[dictionary_status] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } + +} diff --git a/app/api/controller/DictionaryType.php b/app/api/controller/DictionaryType.php new file mode 100644 index 0000000..143e174 --- /dev/null +++ b/app/api/controller/DictionaryType.php @@ -0,0 +1,207 @@ +model = new \app\api\model\DictionaryType(); + } + + + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 +// validate(\app\api\validate\DictionaryType::class)->scene('lists')->check($_get); + + //指定搜索参数 +// $this->model->search_arr = ['username', 'phone:OR', 'sex:OR', 'truename']; + + //数据处理 + $_data = $this->model->index(); + $this->model->exportDictToConfig(); + //返回数据 + return send_http_status($_data); + } + + public function test(){ + + } + + public function read(int $id = 0) + { + //验证参数 + validate(\app\api\validate\DictionaryType::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * @OA\Post (path="demo/add",tags={"测试案例"},summary="添加DEMO数据",description="添加后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了字典类型:[dict_symbol]:[dict_name] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + //验证参数 + validate(\app\api\validate\DictionaryType::class)->check($_post); + + //处理并返回参数 + + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @OA\Post (path="demo/edit/id/{id}",tags={"测试案例"},summary="修改DEMO数据",description="修改后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time]修改了字典[dict_symbol]的数据 + */ + public function edit(int $id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = empty($_post['id']) ? $id : 0; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\DictionaryType::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * @OA\Post ( + * path="demo/delete",tags={"测试案例"},summary="删除DEMO数据",description="根据ID删除DEMO信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了字典:[dict_symbol]:[dict_name] + */ + public function delete() + { + //接收参数 + $ids = $this->request->param('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,1000'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * @OA\Post ( + * path="demo/status",tags={"测试案例"},summary="更新DEMO状态",description="根据ID及status更新DEMO状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 设置ID为[id]测试账号为:[dictionary_status] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } + +} diff --git a/app/api/controller/Group.php b/app/api/controller/Group.php new file mode 100644 index 0000000..54c0885 --- /dev/null +++ b/app/api/controller/Group.php @@ -0,0 +1,395 @@ +request->get(); + + //验证参数 + validate(\app\api\validate\Group::class)->scene('lists')->check($_get); + + $_data = (PermissAuth::getInstance())->getGroups(); + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Get ( + * path="group/getGroupRule",tags={"角色管理"},summary="查询角色权限",description="查询角色所有的权限菜单,并进行标记checked", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="role_id",in="path",required=true,description="角色ID",@OA\Schema (type="integer",format="int32")), + * @OA\Response( + * response=200, + * description="返回管理员用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/MenuMsgExport"), + * ), + * ) + */ + public function getGroupRule() + { + //接受参数 + $_get = $this->request->param('role_id', 0, 'int'); +// //验证参数 +// validate(\app\api\validate\Group::class)->scene('lists')->check($_get); + + $_data = (PermissAuth::getInstance())->getGroupRule($_get); + //返回数据 + return send_http_status($_data); + } + + + public function getUserGroups() + { + $_data = (PermissAuth::getInstance())->getUserGroups(UID); + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Post (path="group/add",tags={"角色管理"},summary="添加角色数据",description="添加后台角色数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/GroupField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录总数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了用户角色:[title] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Group::class)->check($_post); + + //处理并返回参数 + $insertId = (PermissAuth::getInstance())->addGroup($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @OA\Post (path="group/addGroupRule/role_id/{role_id}",tags={"角色管理"},summary="添加角色权限",description="添加后台角色权限", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"menu_id"}, + * @OA\Property(property="menu_id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录总数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @param int $role_id + * @Log [sys_name] 在 [sys_time] 编辑了用户角色:[title]的权限菜单: + */ + public function addGroupRule($role_id = 0) + { + //接收参数 + $menu_id = $this->request->param('menu_id', '', 'string'); + $menu_id = implode(',', $menu_id); + //验证参数 + $_rules = [ + 'role_id|用户角色ID' => 'require|length:1,100', + 'menu_id|菜单id' => 'require|length:1,10000', + ]; + validate($_rules)->check(['role_id' => $role_id, 'menu_id' => $menu_id]); + + //处理并返回参数 + $insertId = (PermissAuth::getInstance())->addGroupRule((int)$role_id, $menu_id); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @param int $id 角色ID + */ + public function readRule($id = '') + { + //验证参数 + validate(\app\api\validate\Demo::class)->scene('read')->check(['id' => $id]); + + //处理数据 + $_data = (PermissAuth::getInstance())->getGroupRule((int)$id); + if (!empty($_data)) { + $_data = get_tree($_data); + } + + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Post (path="group/edit/id/{id}",tags={"角色管理"},summary="修改角色数据",description="修改角色数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/GroupField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time]修改了用户角色:[title] + */ + public function edit($id = 0) + { + //接收参数 + $_post['title'] = $this->request->post('title', ''); + $_post['role_code'] = $this->request->post('role_code', ''); + $_post['remark'] = $this->request->post('remark', ''); + $_post['id'] = $id; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Group::class)->check($_post); + + //处理并返回数据 + return send_http_status('', (PermissAuth::getInstance())->editGroup($_post) ? 203 : 204); + } + + /** + * @OA\Post ( + * path="group/status",tags={"角色管理"},summary="更新角色状态",description="根据ID及status更新角色状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] [dictionary_status]了用户角色:[title] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status', 1, 'int'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', PermissAuth::getInstance()->statusGroup($ids, $status) !== false ? 207 : 208); + } + + /** + * @OA\Post ( + * path="group/delete",tags={"角色管理"},summary="删除角色数据",description="根据ID删除角色数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了用户角色:[title] + */ + public function delete() + { + //接收参数 + $ids = $this->request->post('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,100'])->check(['ids' => $ids]); + + $affected = (PermissAuth::getInstance())->deleteGroup($ids); + + //处理并返回数据 + return send_http_status('', $affected ? 205 : 206); + } + + /** + * swagger response返回数据引用 + * @OA\Schema( + * schema="GroupMsgExport", + * required={"code","msg","count","data"}, + * @OA\Property( + * property="code", + * type="integer", + * format="int32", + * description="状态码" + * ), + * @OA\Property( + * property="msg", + * type="string", + * description="提示消息" + * ), + * @OA\Property( + * property="count", + * type="integer", + * format="int32", + * description="记录总数", + * ), + * @OA\Property( + * property="data", + * type="array", + * description="请求结果", + * @OA\Items( + * @OA\Property( + * property="id", + * type="integer", + * description="ID" + * ), + * @OA\Property( + * property="site_id", + * type="integer", + * description="站点ID" + * ), + * @OA\Property( + * property="remark", + * type="string", + * description="备注", + * ), + * @OA\Property( + * property="title", + * type="string", + * description="用户组中文名称" + * ), + * @OA\Property( + * property="role_code", + * type="string", + * description="角色标识" + * ), + * @OA\Property( + * property="status", + * type="integer", + * description="状态:为1正常,为0禁用" + * ), + * @OA\Property( + * property="rules", + * type="string", + * description="用户组拥有的规则id, 多个规则","隔开" + * ), + * @OA\Property( + * property="type", + * type="integer", + * description="组的类型 1:共用 2:个人" + * ), + * @OA\Property( + * property="delete_time", + * type="integer", + * description="是否删除 null:未删除" + * ), + * @OA\Property( + * property="update_time", + * type="integer", + * description="更新时间" + * ), + * @OA\Property( + * property="create_time", + * type="integer", + * description="创建时间" + * ), + * ) + * ), + * + * ), + */ +} diff --git a/app/api/controller/ImageStorage.php b/app/api/controller/ImageStorage.php new file mode 100644 index 0000000..6e3076c --- /dev/null +++ b/app/api/controller/ImageStorage.php @@ -0,0 +1,85 @@ +model = new \app\api\model\ImageStorages(); + } + + + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //指定搜索参数 + $this->model->search_arr = ['title']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + public function read(int $id = 0) + { + //验证参数 + validate(\app\api\validate\Message::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + public function edit(int $id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = empty($_post['id']) ? $id : 0; //安全赋值,以免客户端没传id,修改需要id字段 + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + public function delete() + { + //接收参数 + $ids = $this->request->param('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,1000'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + +} diff --git a/app/api/controller/Index.php b/app/api/controller/Index.php new file mode 100644 index 0000000..0219a1c --- /dev/null +++ b/app/api/controller/Index.php @@ -0,0 +1,25 @@ +model = new \app\api\model\Log(); + } + /** + * @OA\Get ( + * path="log/index",tags={"日志管理"},summary="获取日志列表",description="根据相关参数获取日志列表", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="username",in="query",description="管理员名称",@OA\Schema (type="string")), + * @OA\Parameter(name="model",in="query",description="操作模块",@OA\Schema (type="string")), + * @OA\Parameter (name="page",in="query",description="当前页码",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="limit",in="query",description="分页数量",@OA\Schema (type="integer",format="int15")), + * @OA\Response( + * response=200, + * description="返回管理员用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/LogMsgExport"), + * ), + * ), + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 + validate(\app\api\validate\Log::class)->check($_get); + + //指定搜索参数 + $this->model->search_arr = ['username','model']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + +} diff --git a/app/api/controller/Login.php b/app/api/controller/Login.php new file mode 100644 index 0000000..6f1e7f2 --- /dev/null +++ b/app/api/controller/Login.php @@ -0,0 +1,169 @@ +param(); + + //验证参数 + $_rule = [ + 'username|登录用户名' => 'require|length:4,20', + 'password|登录密码' => 'require|length:6,20', + 'key|验证码' => 'require|length:6,200', + 'code|验证码' => 'require|length:1,6', + ]; + + $_message = [ + 'username.length' => '请输入4到20位的用户名!!!', + 'password.length' => '请输入6到20位的密码!!!', + 'code.length' => '请输入1到6位的验证码!!!', + 'key.length' => '验证码不正确!!!', + ]; + + env('app_debug') ?: validate($_rule, $_message)->check($_post); + + //验证码检测 + env('app_debug') ?: $this->checkCaptcha($_post['code'], $_post['key']); + + //验证用户信息 + $user_data = $manager->checkLogin($_post); + + //发送JWT签证密钥 + $token_obj = (JwtAuth::getInstance())->setToken($user_data); + + //写入登录日志 + defined('CURR_THIS') ?: define('CURR_THIS', get_called_class()); + $user_data['user_id'] = $user_data['id']; + ActionLog::getInstance()->write($user_data); + + //获取未读系统邮件 + $message = new \app\api\model\Message(); + $message->setUserMessage($user_data['user_id'],$user_data['site_id']); + + //返回数据 + $data = [ + 'access_token' => [ + 'token' => $token_obj->getToken(), + 'expire' => config('jwtauth.web.expire_at'), +// 'refresh_token' => $token_obj->createRefreshToken(new Token()), + ], + 'userinfo' => $user_data, + ]; + return send_http_status($data); + } + + /** + * 通过refresh_token重新生成token + */ + public function refreshToken(Token $token) + { + $refresh_token = request()->param('refresh_token'); + $_data = $token->refreshToken($refresh_token); + if (!empty($_data)) { + return send_http_status($_data); + } + return send_http_status('', 40509, 406); + } + + /** + * @OA\Get ( + * path="login/captcha",tags={"公共分类"},summary="获取验证码",description="获取验证码", + * @OA\Response( + * response=200, + * description="返回验证码信息", + * @OA\JsonContent( + * @OA\Schema( + * required={"code","msg","count","data"}, + * @OA\Property( + * property="code", + * type="integer", + * format="int32", + * description="状态码" + * ), + * @OA\Property( + * property="msg", + * type="string", + * description="提示消息" + * ), + * @OA\Property( + * property="count", + * type="integer", + * format="int32", + * description="记录总数", + * ), + * @OA\Property( + * property="data", + * type="array", + * description="请求结果", + * @OA\Items( + * @OA\Property( + * property="base64", + * type="string", + * description="base64图片" + * ), + * @OA\Property( + * property="key", + * type="string", + * description="验证码安全标识" + * ), + * @OA\Property( + * property="md5", + * type="string", + * description="验证码md5值" + * ), + * ) + * ), + * ), + * ), + * ), + * ) + * 前后端分离验证码生成 + */ + public function captcha() + { + $_data = CaptchaApi::create(); + return send_http_status($_data); + } + + /** + * 前后端分离验证码检测 + * @param string $code + * @param string $key + */ + public function checkCaptcha(string $code = '', string $key = '') + { + return CaptchaApi::check($code, $key) ?: send_http_status('', 40506); + } +} diff --git a/app/api/controller/Manager.php b/app/api/controller/Manager.php new file mode 100644 index 0000000..63978ac --- /dev/null +++ b/app/api/controller/Manager.php @@ -0,0 +1,439 @@ +model = new \app\api\model\Manager(); + } + + /** + * @OA\Get ( + * path="manager/index",tags={"用户管理"},summary="获取用户列表",description="根据相关参数获取用户列表", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="username",in="query",description="管理员名称",@OA\Schema (type="string")), + * @OA\Parameter(name="truename",in="query",description="真实姓名",@OA\Schema (type="string")), + * @OA\Parameter (name="phone",in="query",description="手机号",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="sex",in="query",description="性别",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="page",in="query",description="当前页码",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="limit",in="query",description="分页数量",@OA\Schema (type="integer",format="int15")), + * @OA\Response( + * response=200, + * description="返回管理员用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/ManagerMsgExport"), + * ), + * ) + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //验证参数 + validate(\app\api\validate\Manager::class)->scene('lists')->check($_get); + + //指定搜索参数 + $this->model->search_arr = ['username', 'phone', 'sex', 'truename']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Get ( + * path="manager/read/id/{id}",tags={"用户管理"},summary="获取用户详情",description="根据ID查询用户信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="id",in="path",required=true,description="ID",@OA\Schema (type="integer",format="int32")), + * @OA\Response( + * response=200, + * description="返回管理员用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/ManagerMsgExport"), + * ), + * ) + */ + public function read(int $id = UID) + { + //验证参数 + validate(\app\api\validate\Manager::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * @OA\Get ( + * path="manager/readPersional",tags={"用户管理"},summary="获取个人详情",description="获取当前登录用户的个人资料", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Response( + * response=200, + * description="返回当前登录用户的数据信息", + * @OA\JsonContent(ref="#/components/schemas/ManagerMsgExport"), + * ), + * ) + */ + public function readPersional() + { + //验证参数 + validate(\app\api\validate\Manager::class)->scene('read')->check(['id' => UID]); + + //返回数据 + return send_http_status($this->model->parentRead(UID)); + } + + /** + * @OA\Get ( + * path="manager/permissMenu",tags={"用户管理"},summary="获取用户权限",description="根据当前登录用户UID获取当前用户权限菜单", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Response( + * response=200, + * description="返回管理员权限菜单数据", + * @OA\JsonContent(ref="#/components/schemas/MenuMsgExport"), + * ), + * ) + */ + public function permissMenu() + { + $_data = PermissAuth::getInstance()->getAuthList(UID, 1, true); + + //用户权限 + $_data = get_tree($_data, 0, 'children'); + + //返回数据 + return send_http_status($_data); + } + + /** + * @OA\Post (path="manager/add",tags={"用户管理"},summary="添加管理员数据",description="添加后台管理员数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ManagerField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录总数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了管理员:[username] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Manager::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @OA\Post (path="manager/edit/id/{id}",tags={"用户管理"},summary="修改用户数据",description="修改管理员数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ManagerField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time] 修改了管理员账号[username]的数据 + */ + public function edit($id = 0) + { + //接收参数 + $_post = $this->request->param(); + + //验证参数 + validate(\app\api\validate\Manager::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + /** + * @OA\Post (path="manager/editPersional",tags={"用户管理"},summary="修改个人数据",description="修改自己的个人资料", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ManagerField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time] 修改了管理员账号[username]的数据 + */ + public function editPersional() + { + //接收参数 + $_post = $this->request->param(); + + //验证参数 + validate(\app\api\validate\Manager::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, UID) ? 203 : 204); + } + + /** + * @OA\Post ( + * path="manager/passwordEdit",tags={"用户管理"},summary="重置用户密码",description="根据ID重置用户密码", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","password","old_password","username"}, + * @OA\Property(property="id",type="integer",format="int15",description="用户ID"), + * @OA\Property(property="password",type="string",minLength=6,maxLength=20,description="新密码"), + * @OA\Property(property="old_password",type="string",minLength=6,maxLength=20,description="原始密码"), + * @OA\Property(property="username",type="string",minLength=2,maxLength=20,description="管理员账号"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time] 修改了账号密码 + */ + public function passwordEdit() + { + //接收参数 + $_post = $this->request->param(); + $_post['id'] = UID; + + //验证参数 + validate(\app\api\validate\Manager::class)->scene('UpdatePassword')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, UID) ? 203 : 204); + } + + /** + * @OA\Post ( + * path="manager/passwordReset",tags={"用户管理"},summary="重置用户密码",description="根据ID重置用户密码", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","password","username"}, + * @OA\Property(property="id",type="integer",format="int15",description="用户ID"), + * @OA\Property(property="password",type="string",minLength=6,maxLength=20,description="管理员密码"), + * @OA\Property(property="username",type="string",minLength=2,maxLength=20,description="管理员账号"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time] 重置了[username]的账号密码为:[password] + */ + public function passwordReset() + { + //接收参数 + $_post = $this->request->only(['id', 'password', 'username']); + + //验证参数 + validate(\app\api\validate\Manager::class)->scene('ResetPassword')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $_post['id']) ? 203 : 204); + } + + /** + * @OA\Post ( + * path="manager/delete",tags={"用户管理"},summary="删除用户数据",description="根据ID删除用户数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了管理员账号:[username] + */ + public function delete() + { + //接收参数 + $ids = $this->request->post('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,100'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * @OA\Post ( + * path="manager/status",tags={"用户管理"},summary="更新用户状态",description="根据ID及status更新用户状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] [dictionary_status]了管理员:[username], 管理员ID为:[id] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } + + + public function getMessage(){ + $type = $this->request->param('type','98'); + + $model = new \app\api\model\Message(); + $_data = $model->getUserMessage($type); + return send_http_status($_data); + } + + public function messageStatus() + { + //接收参数 + $ids = $this->request->param('id'); + $type = $this->request->param('type'); + $model = new \app\api\model\Message(); + + //处理并返回数据 + return send_http_status('',$model->setMessageRead($ids,$type) !== false ? 207 : 208); + } + + public function messageDelete(){ + //接收参数 + $ids = $this->request->param('id'); + $model = new \app\api\model\Message(); + + //处理并返回数据 + return send_http_status('',$model->setMessageDelete($ids) ? 205 : 206); + } + + + public function setMessage(){ + + //获取未读系统邮件 + $message = new \app\api\model\Message(); + $message->setUserMessage(); + + return send_http_status('', 200); + + + } +} diff --git a/app/api/controller/Menu.php b/app/api/controller/Menu.php new file mode 100644 index 0000000..e2d35af --- /dev/null +++ b/app/api/controller/Menu.php @@ -0,0 +1,256 @@ +model = new \app\api\model\Menu(); + } + /** + * @OA\Get ( + * path="menu/index",tags={"权限菜单"},summary="获取菜单列表",description="根据相关参数获取菜单列表", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="path",in="query",description="前端权限路由",@OA\Schema (type="string")), + * @OA\Parameter(name="name",in="query",description="权限菜单名称",@OA\Schema (type="string")), + * @OA\Parameter (name="page",in="query",description="当前页码",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="limit",in="query",description="分页数量",@OA\Schema (type="integer",format="int15")), + * @OA\Response( + * response=200, + * description="返回权限菜单菜单数据信息", + * @OA\JsonContent(ref="#/components/schemas/MenuMsgExport"), + * ), + * ) + */ + public function index() + { + //接受参数 + $_get = $this->request->param(); + + //指定搜索参数 + $this->model->search_arr = ['path', 'name']; + + //验证参数 + validate(\app\api\validate\Menu::class)->scene('lists')->check($_get); + + $_data = $this->model->index(); + + //数据处理 前台处理 +// $_data['data'] = get_tree($_data['data'], 0, 'children'); + + //返回数据 + return send_http_status($_data); + } + + + /** + * @OA\Post (path="menu/add",tags={"权限菜单"},summary="添加权限菜单数据",description="添加后台权限菜单数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/MenuField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录总数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了权限菜单:[name] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 + validate(\app\api\validate\Menu::class)->check($_post); + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + + /** + * @OA\Get ( + * path="menu/read/id/{id}",tags={"权限菜单"},summary="获取菜单详情",description="根据ID查询菜单信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="id",in="path",required=true,description="ID",@OA\Schema (type="integer",format="int32")), + * @OA\Response( + * response=200, + * description="返回权限菜单菜单数据信息", + * @OA\JsonContent(ref="#/components/schemas/MenuMsgExport"), + * ), + * ) + */ + public function read($id = '') + { + //验证参数 + validate(\app\api\validate\Menu::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * @OA\Post (path="menu/edit/id/{id}",tags={"权限菜单"},summary="修改菜单数据",description="修改权限菜单数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/MenuField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time] 修改了权限菜单[name]的数据 + */ + public function edit($id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = $id; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 + validate(\app\api\validate\Menu::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * @OA\Post ( + * path="menu/delete",tags={"权限菜单"},summary="删除菜单数据",description="根据ID删除菜单数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了权限菜单:[name] + */ + public function delete() + { + //接收参数 + $ids = $this->request->param('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,100'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * @OA\Post ( + * path="menu/status",tags={"权限菜单"},summary="更新菜单状态",description="根据ID及status更新菜单状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] [dictionary_status]了权限菜单:[name] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } +} diff --git a/app/api/controller/Message.php b/app/api/controller/Message.php new file mode 100644 index 0000000..ac29b5b --- /dev/null +++ b/app/api/controller/Message.php @@ -0,0 +1,200 @@ +model = new \app\api\model\Message(); + } + + + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //指定搜索参数 + $this->model->search_arr = ['title']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + public function read(int $id = 0) + { + //验证参数 + validate(\app\api\validate\Message::class)->scene('read')->check(['id' => $id]); + + //返回数据 + return send_http_status($this->model->parentRead($id)); + } + + /** + * @OA\Post (path="demo/add",tags={"测试案例"},summary="添加DEMO数据",description="添加后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="添加成功", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="integer",format="int32",description="添加成功的的数据ID"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 创建了字典类型:[dict_symbol]:[dict_name] + */ + public function add() + { + //接收参数 + $_post = $this->request->post(); + + //验证参数 +// validate(\app\api\validate\Dictionary::class)->check($_post); + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + /** + * @OA\Post (path="demo/edit/id/{id}",tags={"测试案例"},summary="修改DEMO数据",description="修改后台DEMO管理数据", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/DemoField"), + * ), + * ), + * @OA\Response( + * response=200, + * description="修改数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name]在[sys_time]修改了字典[dict_symbol]的数据 + */ + public function edit(int $id = 0) + { + //接收参数 + $_post = $this->request->post(); + $_post['id'] = empty($_post['id']) ? $id : 0; //安全赋值,以免客户端没传id,修改需要id字段 + + //验证参数 +// validate(\app\api\validate\Dictionary::class)->scene('update')->check($_post); + + //处理并返回数据 + return send_http_status('', $this->model->parentEdit($_post, $id) ? 203 : 204); + } + + + /** + * @OA\Post ( + * path="demo/delete",tags={"测试案例"},summary="删除DEMO数据",description="根据ID删除DEMO信息", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 删除了字典:[dict_symbol]:[dict_name] + */ + public function delete() + { + //接收参数 + $ids = $this->request->param('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,1000'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + /** + * @OA\Post ( + * path="demo/status",tags={"测试案例"},summary="更新DEMO状态",description="根据ID及status更新DEMO状态", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\RequestBody( + * @OA\MediaType(mediaType="application/json", + * @OA\Schema ( + * required={"id","status"}, + * @OA\Property(property="id",type="string",description="数据ID或数据ID集合 id=1或 id=1,2,8"), + * @OA\Property(property="status",type="integer",format="int15",description="数据状态 0禁用 1启用"), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="删除数据状态", + * @OA\JsonContent(type="object", + * @OA\Property(property="code", type="integer",format="int32",description="状态码"), + * @OA\Property(property="msg", type="string",description="提示信息"), + * @OA\Property(property="count", type="integer",format="int32",description="记录数"), + * @OA\Property(property="data", type="string",description="不返回"), + * ), + * ), + * ) + * @Log [sys_name] 在 [sys_time] 设置ID为[id]测试账号为:[dictionary_status] + */ + public function status() + { + //接收参数 + $ids = $this->request->param('id'); + $status = $this->request->param('status'); + + //验证参数 + $_rules = [ + 'ids|id' => 'require|length:1,100', + 'status|状态值' => 'require|number|length:1', + ]; + validate($_rules)->check(['ids' => $ids, 'status' => $status]); + + //处理并返回数据 + return send_http_status('', $this->model->parentStatus($ids, $status) !== false ? 207 : 208); + } + +} diff --git a/app/api/controller/Swagger.php b/app/api/controller/Swagger.php new file mode 100644 index 0000000..1e4aa19 --- /dev/null +++ b/app/api/controller/Swagger.php @@ -0,0 +1,23 @@ +server()['HTTP_HOST'] . '/openapi.json'; + } + } + +} diff --git a/app/api/controller/Test.php b/app/api/controller/Test.php new file mode 100644 index 0000000..3a670ed --- /dev/null +++ b/app/api/controller/Test.php @@ -0,0 +1,71 @@ +setUsername('service@huamill.com'); + $transport->setPassword('Jiabaiyan1018'); + + //实例化mailer + $mailer = new \Swift_Mailer($transport); + + //邮件内容 + $message = new \Swift_Message('测试swiftmailer邮件类'); + $message->setContentType('text/html'); + $message->setFrom(['service@huamill.com' => 'cloud']); + $message->setTo('243993898@qq.com'); + $message->setBody('这是邮件内容,收到请勿回复'); + + //发送邮件 + $result = $mailer->send($message); + + halt($result); + } + + /** + * 发送短信验证码 + * @param int $phone + */ + public function sendSms(string $phone = '') + { + halt(CaptchaApi::createSMS($phone)); + } + + public function checkSms(string $code = '', string $phone = '') + { + halt(CaptchaApi::checkSMS($code, $phone)); + } + + /** + * 日志类测试 + * @Log [sys_name] 在 [sys_time]新增插件数据 + */ + public function writeLog() + { + //手动写入日志数据 + ActionLog::getInstance()->write(); + } + + /** + * composer require jenssegers/agent 获取系统信息,访问终端判断、系统设备等信息 + */ + public function systemInfo() + { + echo 'system'; + } + +} diff --git a/app/api/controller/UploadLog.php b/app/api/controller/UploadLog.php new file mode 100644 index 0000000..c4a43fb --- /dev/null +++ b/app/api/controller/UploadLog.php @@ -0,0 +1,111 @@ +model = new \app\api\model\UploadLog(); + } + /** + * @OA\Get ( + * path="log/index",tags={"上传日志管理"},summary="获取上传日志列表",description="根据相关参数获取日志列表", + * @OA\Parameter(ref="#/components/parameters/token"), + * @OA\Parameter(name="username",in="query",description="管理员名称",@OA\Schema (type="string")), + * @OA\Parameter(name="model",in="query",description="操作模块",@OA\Schema (type="string")), + * @OA\Parameter (name="page",in="query",description="当前页码",@OA\Schema (type="integer",format="int15")), + * @OA\Parameter (name="limit",in="query",description="分页数量",@OA\Schema (type="integer",format="int15")), + * @OA\Response( + * response=200, + * description="返回管理员用户数据信息", + * @OA\JsonContent(ref="#/components/schemas/LogMsgExport"), + * ), + * ), + */ + public function index() + { + //接受参数 + $_get = $this->request->get(); + + //指定搜索参数 +// $this->model->search_arr = ['current']; + + //数据处理 + $_data = $this->model->index(); + + //返回数据 + return send_http_status($_data); + } + + public function add() + { + //接收参数 + $_post = $this->request->post(); + + + //处理并返回参数 + $insertId = $this->model->parentAdd($_post); + return send_http_status($insertId, $insertId ? 201 : 202); + } + + + /** + * @Log [sys_name]在[sys_time] 删除了文件数据 + */ + + public function delete() + { + //接收参数 + $ids = $this->request->param('id'); + + //验证参数 + validate(['ids|id' => 'require|length:1,1000'])->check(['ids' => $ids]); + + //处理并返回数据 + return send_http_status('', $this->model->parentDel($ids) ? 205 : 206); + } + + public function getSiteLists(){ + //数据处理 + $_data = $this->model->getLists(); + + //返回数据 + return send_http_status($_data); + } + + + public function getTagLists(){ + $tagLists = config('dict.sqlfields.image_storage_type'); + $senddData = []; + foreach ($tagLists as $key => $val){ + $item = []; + $item['title'] = $key; + $item['name'] = $val; + $senddData[] = $item; + } + return send_http_status($senddData); + } + + public function getStorageImages(){ + $model = new ImageStorages(); + $model->search_arr = ['title']; + $_data = $model->getStorgaeImagesLists(); + + //返回数据 + return send_http_status($_data); + + } + +} diff --git a/app/api/controller/v1/Test.php b/app/api/controller/v1/Test.php new file mode 100644 index 0000000..7bdb63b --- /dev/null +++ b/app/api/controller/v1/Test.php @@ -0,0 +1,96 @@ +request->param('name', ''); + $_op = $this->where('site_id', SITE_ID); //没有默认where查询 这个一定要 否则获取不到下面的getOptions查询条件值 + if ($name) { + $_op->whereLike('name', "%{$name}%"); + } + $_op->order('create_time DESC'); + return parent::parentLists($_op); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + + } + + /** + * 新增前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeInsert(Model $model) + { + //code + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + + } + + /** + * 获取广告数据 + * @return array|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function AdData() + { + $adid = $this->request->param('adid', ''); + + $_op = Db::name($this->_data_table)->where('site_id', SITE_ID); //没有默认where查询 这个一定要 否则获取不到下面的getOptions查询条件值 + if ($adid) { + $_op->whereLike('adid', $adid); + } + + $_op->order('create_time DESC'); + $data = parent::parentLists($_op); + $result = $data['data']; + + //查询广告位限制的图片宽度、高度 + $_ad = Db::name('ad')->field('width,height')->where('id',$adid)->find(); + array_walk($result, function (&$v) use ($_ad) { + $v['create_time'] = date('Y-m-d H:i:s', $v['create_time']); + $v['start_time'] = date('Y-m-d H:i:s', $v['start_time']); + $v['end_time'] = date('Y-m-d H:i:s', $v['end_time']); + $v['thumb'] = config('dictionary.upload.cdn_img_host') . $v['thumb']; + $v['crop_thumb'] = crop_thumb($v['thumb'], (int)$_ad['width'], (int)$_ad['height']); + }); + $data['data'] = $result; + return $data; + } + + +} diff --git a/app/api/model/AdData.php b/app/api/model/AdData.php new file mode 100644 index 0000000..074b7bc --- /dev/null +++ b/app/api/model/AdData.php @@ -0,0 +1,78 @@ +request->param('name', ''); + $_op = $this->where('site_id', SITE_ID); //没有默认where查询 这个一定要 否则获取不到下面的getOptions查询条件值 + if ($name) { + $_op->whereLike('name', "%{$name}%"); + } + $_op->order('create_time DESC'); + return parent::parentLists($_op); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + if (!in_array(request()->action(), ['read','dataRead'])) { + return; + } + //成时间戳转化时间字符串 + $model->setAttr('end_time', date('Y-m-d H:i:s',$model->getAttr('end_time'))); + $model->setAttr('start_time', date('Y-m-d H:i:s',$model->getAttr('start_time'))); + } + + /** + * 新增前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeInsert(Model $model) + { + +// //时间字符串转化成时间戳 + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + if (!in_array(request()->action(), ['add', 'edit','dataEdit','dataAdd'])) { + return; + } + //时间字符串转化成时间戳 + $model->setAttr('end_time', strtotime($model->getAttr('end_time'))); + $model->setAttr('start_time', strtotime($model->getAttr('start_time'))); + } + +} diff --git a/app/api/model/Block.php b/app/api/model/Block.php new file mode 100644 index 0000000..54649a7 --- /dev/null +++ b/app/api/model/Block.php @@ -0,0 +1,66 @@ +request->param('name', ''); + $_op = $this->where('site_id', SITE_ID); //没有默认where查询 这个一定要 否则获取不到下面的getOptions查询条件值 + if ($name) { + $_op->whereLike('name', "%{$name}%"); + } + $_op->order('sort DESC,create_time DESC'); + return parent::parentLists($_op); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + + } + + /** + * 新增前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeInsert(Model $model) + { + //code + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + + } + +} diff --git a/app/api/model/Column.php b/app/api/model/Column.php new file mode 100644 index 0000000..c9e7bcc --- /dev/null +++ b/app/api/model/Column.php @@ -0,0 +1,88 @@ +request->param('username', ''); + $sex = $this->request->param('sex', '', 'intval'); + $truename = $this->request->param('truename', ''); + $_op = $this->where('site_id', SITE_ID); //没有默认where查询 这个一定要 否则获取不到下面的getOptions查询条件值 + if ($username) { + $_op->whereLike('username', "%{$username}%"); + } + $data = parent::parentLists($_op); + + //获取栏目类型 + $_column_type = config('dictionary.sqlfields.column_type'); + $_model = Db::name('model')->column('*', 'id'); + $data_tmp = $data['data']; + + array_walk($data_tmp, function (&$v) use ($_model, $_column_type) { + $v['model_name'] = $_model[$v['model_id']]['name']; + $v['column_type_name'] = $_column_type[$v['type']]; + }); + $data['data'] = $data_tmp; +// $data = tree($data); + return $data; + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + $model->setAttr('open', true); + } + + /** + * 新增前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeInsert(Model $model) + { + //code + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + + } + + public static function onAfterDelete(Model $model) + { + //删除父级栏目下所有子栏目 + self::destroy(function ($query) use ($model) { + $query->where('child_list', 'like', $model->getAttr('child_list') . '%'); + }); + parent::onAfterDelete($model); + } + +} diff --git a/app/api/model/Common.php b/app/api/model/Common.php new file mode 100644 index 0000000..9afde13 --- /dev/null +++ b/app/api/model/Common.php @@ -0,0 +1,270 @@ +request = request(); + } + + /** + * 获取单条数据基类模型 + * @param string $id + * @param null $_op 模型链式对象 + * @return array|Model|null + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function parentRead($id = '', $_op = null) + { + if (!is_object($_op)) { + $_op = $this; + } + return $_op->where('site_id', SITE_ID)->find($id); + } + + /** + * 获取数据列表基类模型 + * @param null $_op 模型链式对象 + * @param int $ispage 是否分页,默认分页 + * @return array|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + protected function parentLists($_op = null, $ispage = 1,$issite=true) + { + //模型对象为空,设定默认查询对象 + if($issite){ + if (!is_object($_op) || empty($_op)) { + $_op = $this->where('site_id', SITE_ID); //没有默认where查询 这个一定要 否则获取不到下面的getOptions查询条件值 + } else { + $_op->where('site_id', SITE_ID); //强制绑定站点参数 + } + } + + + //公共搜索查询 + if (count($this->search_arr) > 0) { + $_param = request()->param(); //搜索参数集合 + $_op->where(function ($query) use ($_param) { + foreach ($this->search_arr as $k => $v) { + $search_arr = explode(':', $v); + $search_name = $search_arr[0]; + $logic = $search_arr[1] ?? 'AND'; + if (array_key_exists($search_name, $_param)) { + if (!empty($_param[$search_name])) { + $query->whereLike($search_name, "%{$_param[$search_name]}%", $logic); + } + } + } + }); + } + + //执行分页查询 + $page = $this->request->param('page', config('page')); + $limit = $this->request->param('limit', config('limit')); + $count = $_op->count(); + + $this->listsOrder($_op); //统一排序 + + //无分页判断 + $ispage != 1 ?: $_op->page((int)$page, (int)$limit); + + $_data = [ + 'data' => $_op->select()->toArray(), + 'page_total' => $count, + ]; + return $_data; + } + + /** + * 数据排序 + */ + private function listsOrder(&$_op) + { + if ($_op->getOptions('order')) { + return; + } + $sort = $this->request->get('sort'); + $sort = in_array($sort, $this->checkAllowFields()) ? $sort : ''; + $order = $this->request->get('order', 'DESC', 'strtoupper') == 'ASC' ? 'ASC' : 'DESC'; + $sort_order = $sort . ' ' . $order; + if (empty($sort)) { + $sort_order = in_array('sort', $this->checkAllowFields()) ? 'sort DESC,create_time DESC' : 'create_time DESC'; + } + $_op->order($sort_order); + } + + /** + * 新增数据基类模型 + * @param array $_param + * @return Common|Model + */ + public function parentAdd($_param = []) + { + if (empty($_param)) { + $_param = $this->request->post(); + } + $_param['site_id'] = SITE_ID; + + $_result = self::create($_param); + return $_result->id; + } + + /** + * 修改基类模型 + * @param array $_data + * @param null $_op 模型链式对象 + * @param $id + * @return bool|\type + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function parentEdit($_data = [], $id = 0, $_op = null) + { + if (empty($_data)) { + $_data = $this->request->param(); + } + if (!is_object($_op)) { + $_op = $this; + } + $_op = $_op->where('site_id', SITE_ID)->find($id); + if (empty($_op)) { + return send_http_status('', 101); + } + $_data['site_id'] = SITE_ID; //防止篡改站点 + + return $_op->save($_data); + } + + /** + * 删除 + * @param $id + * @param null $_op 模型链式对象 + * @param false $true_delete 是否真删除 默认软删除 + * @return bool|\type + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function parentDel($id, $_op = null, bool $true_delete = false) + { + if (!is_object($_op)) { + $_op = $this; + } + $ids = explode(',', $id); + + $_op = $_op->where('id', 'in', $ids)->where('site_id', SITE_ID)->select(); + if ($_op->isEmpty()) { + return send_http_status('', 101); + } + return self::destroy($ids, $true_delete); //TP软删除 + } + + /** + * 更改数据状态 + * @param $id + * @param $status + * @param null $_op + * @return \type + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function parentStatus($id, $status, $_op = null) + { + if (!is_object($_op)) { + $_op = $this; + } + $ids = explode(',', $id); + $_op = $_op->where('id', 'in', $ids)->where('site_id', SITE_ID)->select(); + if ($_op->isEmpty()) { + return send_http_status('', 101); + } + $_where = [ + ['id', 'in', $ids], + ['site_id', '=', SITE_ID], + ]; +// $affected = self::where('id', 'in', $ids)->where('site_id', SITE_ID)->update(['status' => $status]); + $affected = self::update(['status' => $status], $_where); + return $affected; + } + + public static function onAfterRead(Model $model) + { + $model->offsetUnset('delete_time'); + } + + public static function onBeforeWrite(Model $model) + { + //以下参数禁止新增或更改 + $model->setAttr('copy_safe_id', (int)$model->getAttr('id')); //设定副本参数 + $model->setAttr('copy_safe_site_id', (int)$model->getAttr('site_id')); //设定副本参数 + $model->offsetUnset('id'); + $model->offsetUnset('site_id'); + $model->offsetUnset('delete_time'); + $model->offsetUnset('update_time'); + $model->offsetUnset('create_time'); + } + + + public static function onAfterWrite(Model $model) + { + //增加创建和修改日志记录 + ActionLog::getInstance()->write($model->getData(), $model->getOrigin(), $model->getLastSql()); + + } + + public static function onAfterDelete(Model $model) + { + //增加删除日志记录 + ActionLog::getInstance()->write($model->getData(), [], $model->getLastSql()); + } + +} diff --git a/app/api/model/Config.php b/app/api/model/Config.php new file mode 100644 index 0000000..a3f87b7 --- /dev/null +++ b/app/api/model/Config.php @@ -0,0 +1,119 @@ +where('name', 'in', $name_arr)->where('site_id', SITE_ID)->column('id'); + if (!empty($id_arr)) { + $affected = self::destroy($id_arr, true); //TP真删除 + if (!$affected) { + return false; + } + } + foreach ($_post as $k => $v) { + $_temp = [ + 'name' => $k, + 'value' => $v, + 'site_id' => SITE_ID, + 'create_time' => time(), + 'update_time' => time(), + ]; + $_data[] = $_temp; + } + return $this->insertAll($_data); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + //读取数据前格式化数据 + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + //业务逻辑 + } + +} diff --git a/app/api/model/Demo.php b/app/api/model/Demo.php new file mode 100644 index 0000000..48945c9 --- /dev/null +++ b/app/api/model/Demo.php @@ -0,0 +1,137 @@ +request->param('username', ''); +// $_op = $this->whereLike('username', "{$username}%"); +// return parent::parentLists($_op); + + //通用逻辑 + return parent::parentLists(); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + //读取数据前格式化数据 + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + //业务逻辑 + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + parent::onBeforeWrite($model); + + //写入数据前格式化数据 + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + //业务逻辑 + // code... + + //以下参数禁止新增或者修改 + $model->offsetUnset('ip'); + } + +} diff --git a/app/api/model/Dictionary.php b/app/api/model/Dictionary.php new file mode 100644 index 0000000..a936450 --- /dev/null +++ b/app/api/model/Dictionary.php @@ -0,0 +1,108 @@ +request->param('dict_name', ''); + $_op = $this->whereLike('dict_name', "{$dict_name}%"); + + $dict_value = $this->request->param('dict_value', ''); + $_op = $this->whereLike('dict_value', "{dict_value}%"); + + //自定义处理逻辑 + $dict_type_id = $this->request->param('dict_type_id', ''); + $_op = $this->where('dict_type_id', $dict_type_id); + + //通用逻辑 + return parent::parentLists($_op); + } + + public function chekName($name) + { + //自定义处理逻辑 + $dict_type_id = $this->request->param('dict_type_id', ''); + $id = $this->request->param('id', ''); + $codition = [ + 'dict_value' => $name, + 'dict_type_id' => $dict_type_id, + + ]; + + if($id >0){ + $count = $this->where($codition)->where('id','<>',$id)->count(); + }else{ + $count = $this->where($codition)->count(); + } + + return $count; + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + //读取数据前格式化数据 + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + //业务逻辑 + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + parent::onBeforeWrite($model); + + //写入数据前格式化数据 + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + //业务逻辑 + // code... + } + + public static function onAfterDelete(Model $model) + { + parent::onAfterDelete($model); // TODO: Change the autogenerated stub + $dic = new DictionaryType(); + $dic->exportDictToConfig(); + } + + public static function onAfterWrite(Model $model) + { + parent::onAfterWrite($model); // TODO: Change the autogenerated stub + $dic = new DictionaryType(); + $dic->exportDictToConfig(); + } + + +} diff --git a/app/api/model/DictionaryType.php b/app/api/model/DictionaryType.php new file mode 100644 index 0000000..5486c50 --- /dev/null +++ b/app/api/model/DictionaryType.php @@ -0,0 +1,119 @@ +request->param('username', ''); +// $_op = $this->whereLike('username', "{$username}%"); + //通用逻辑 + return parent::parentLists(); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + //读取数据前格式化数据 + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + //业务逻辑 + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + parent::onBeforeWrite($model); + + //写入数据前格式化数据 + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + //业务逻辑 + // code... + } + + + public static function onAfterDelete(Model $model) + { + parent::onAfterDelete($model); // TODO: Change the autogenerated stub + //删除字典类型后 下属的字典值全部删除 + $dict_type_id = $model->getAttr('id'); + $diction = new Dictionary(); + $diction->destroy(['dict_type_id'=>$dict_type_id]); + $model->exportDictToConfig(); + + } + + public static function onAfterWrite(Model $model) + { + parent::onAfterWrite($model); // TODO: Change the autogenerated stub + $model->exportDictToConfig(); + } + + + + public function exportDictToConfig(){ + $dict_datas = $this->field('a.dict_symbol,b.dict_name,b.dict_value')->alias('a') + ->join(['system_dictionary'=>'b'],'a.id=b.dict_type_id','left') + ->where('b.delete_time','null') + ->select() + ; + $datas = []; + foreach ($dict_datas as $item){ + $datas[$item['dict_symbol']][$item['dict_value']] = $item['dict_name']; + } + $path = \think\facade\App::getConfigPath().'dict.php'; + $ditc_text = " + + ".var_export($datas,true)." +, +]; + "; + if (fopen($path,'w')){ + file_put_contents($path,$ditc_text); + } + + } + + + +} diff --git a/app/api/model/ImageStorages.php b/app/api/model/ImageStorages.php new file mode 100644 index 0000000..3d66aea --- /dev/null +++ b/app/api/model/ImageStorages.php @@ -0,0 +1,46 @@ +request->param('type', 0); + $_op = $this->field('*'); + + if($type){ + $_op = $this->where('type',$type); + } + + //执行通用查询 + return parent::parentLists($_op); + } + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + $typeArr = config('dict.sqlfields.image_storage_type'); + + $model->setAttr('type_str',$typeArr[$model->getAttr('type')]); + + } + +} diff --git a/app/api/model/Log.php b/app/api/model/Log.php new file mode 100644 index 0000000..5656940 --- /dev/null +++ b/app/api/model/Log.php @@ -0,0 +1,188 @@ +request->param('create_time_start', ''); + $end_time = $this->request->param('create_time_end', ''); + if ($start_time && $end_time) { + $_op = $this->whereTime('create_time', '>=', strtotime($start_time)); + $_op->whereTime('create_time', '<=', strtotime('+1 day', strtotime($end_time))); + } + return parent::parentLists(!empty($_op) ? $_op : ''); + } + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + $model->offsetSet('action_ip', long2ip((int)$model->getAttr('action_ip'))); + } + +} diff --git a/app/api/model/Manager.php b/app/api/model/Manager.php new file mode 100644 index 0000000..4dd96ba --- /dev/null +++ b/app/api/model/Manager.php @@ -0,0 +1,289 @@ +checkSite(); + + $_data = self::where('username', $_post['username'])->where('site_id', $site_id)->find(); + if (empty($_data)) { + return send_http_status('', 40502); + } + + if ($_data['status'] != 1) { + return send_http_status('', 40503); + } + + if (!password_verify($_post['password'], $_data['password'])) { + return send_http_status('', 40504); + } + //登录成功写入登录信息 + $this->setLoginIp($_data['id'], $site_id); + + return $_data->hidden(['password'])->toArray(); + } + + + /** + * 用户最后登录 IP + */ + private function setLoginIp(int $user_id = 0, int $site_id = 0) + { + $_data = [ + 'ip' => ip2long(request()->ip()), + 'login_time' => time(), + ]; + $_op = $this->where('site_id', $site_id)->where('id', $user_id); + return $_op->save($_data); + } + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + $model->hidden(['password']); + $model->offsetSet('ip', long2ip((int)$model->getAttr('ip'))); + $model->offsetSet('sexname', config('dictionary.sqlfields.sex')[$model->getAttr('sex')]); + //用户权限 + $model->offsetSet('authorities', PermissAuth::getInstance()->getAuthList($model->getAttr('id'), 1, true)); + //用户角色 + $_roles = PermissAuth::getInstance()->getUserGroups($model->getAttr('id')); + $model->offsetSet('roles', $_roles); + $model->offsetSet('role_ids', implode(',', array_column($_roles, 'id'))); + //省市区代码转换 + $model->setAttr('province_city', [$model->getAttr('province'), $model->getAttr('city'), $model->getAttr('area')]); + } + + /** + * 新增前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeInsert(Model $model) + { + if (!in_array(request()->action(), ['add'])) { + return true; + } + //密码加密 + $salt_password = password_hash($model->getAttr('password'), PASSWORD_DEFAULT); + $model->setAttr('password', $salt_password); + } + + /** + * 修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeUpdate(Model $model) + { + //禁止修改用户名 + $model->offsetUnset('username'); + + $action = request()->action(); + + if (!in_array($action, ['edit', 'passwordEdit', 'passwordReset', 'editPersional'])) { + return true; + } + + //密码修改验证逻辑 + if ($action == 'passwordEdit') { + if (!password_verify($model->getData('old_password'), $model->getOrigin('password'))) { + return send_http_status('', 40504); + } + } + + //密码加密 + if ($action == 'passwordEdit' || $action == 'passwordReset') { + $salt_password = password_hash($model->getData('password'), PASSWORD_DEFAULT); + $model->setAttr('password', $salt_password); + } + + //省市区代码拆分 + $city_arr = $model->getAttr('province_city'); + if (!empty($city_arr)) { + $model->setAttr('province', $city_arr[0]); + $model->setAttr('city', $city_arr[1]); + $model->setAttr('area', $city_arr[2]); + } + + } + + /** + * 新增前、修改前 同时 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + parent::onBeforeWrite($model); + + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + //以下参数禁止新增或更改 + $model->offsetUnset('ip'); + } + + public static function onAfterWrite(Model $model) + { + parent::onAfterWrite($model); + + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + //写入用户权限 + $id = $model->getAttr('id') ?? $model->getAttr('copy_safe_id'); + PermissAuth::getInstance()->addUserGroupAccess((int)$id, $model->getAttr('role_ids')); + } + + + +} diff --git a/app/api/model/Menu.php b/app/api/model/Menu.php new file mode 100644 index 0000000..8aa9111 --- /dev/null +++ b/app/api/model/Menu.php @@ -0,0 +1,226 @@ +where('is_dev', 0); //开发者模式 + return parent::parentLists($_op, 0); + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + //设定child_list + if (empty($model->getAttr('parent_id'))) { + //parent_id = 0 一级栏目填充 + $child_list = self::where('parent_id', 0)->max('child_list'); + $child_list = !empty($child_list) ? (int)$child_list + 1 : 100; //如果不存在设置默认值 100起始 + $model->setAttr('child_list', $child_list); + } else { + //子栏目填充 + $child_list = self::where('parent_id', $model->getAttr('parent_id'))->max('child_list'); //同级最大 + $parent_child_list = self::where('id', $model->getAttr('parent_id'))->value('child_list'); //父级初始 + $child_list = !empty($child_list) ? (int)$child_list + 1 : $parent_child_list . '100'; + $model->setAttr('child_list', $child_list); + } + parent::onBeforeWrite($model); + } + + + public static function onAfterDelete(Model $model) + { + //删除父级栏目下所有子栏目 + if ($model->getAttr('child_list')) { + self::where('child_list', 'like', $model->getAttr('child_list') . '%')->useSoftDelete('delete_time', time())->delete(); + } + parent::onAfterDelete($model); + } + + /** + * swagger response返回数据引用 + * @OA\Schema( + * schema="MenuMsgExport", + * required={"code","msg","count","data"}, + * @OA\Property( + * property="code", + * type="integer", + * format="int32", + * description="状态码" + * ), + * @OA\Property( + * property="msg", + * type="string", + * description="提示消息" + * ), + * @OA\Property( + * property="count", + * type="integer", + * format="int32", + * description="记录总数", + * ), + * @OA\Property( + * property="data", + * type="array", + * description="请求结果", + * @OA\Items( + * @OA\Property( + * property="id", + * type="integer", + * description="菜单ID" + * ), + * @OA\Property( + * property="site_id", + * type="integer", + * description="站点ID" + * ), + * @OA\Property( + * property="name", + * type="string", + * description="菜单名称", + * ), + * @OA\Property( + * property="parent_id", + * type="integer", + * description="父级ID", + * ), + * @OA\Property( + * property="model_name", + * type="string", + * description="模块名/控制器/方法" + * ), + * @OA\Property( + * property="path", + * type="string", + * description="对应前台的模板的路由地址" + * ), + * @OA\Property( + * property="component", + * type="string", + * description="前台对应的组件路径", + * ), + * @OA\Property( + * property="authority", + * type="string", + * description="按钮权限标识", + * ), + * @OA\Property( + * property="icon", + * type="string", + * description="菜单图标", + * ), + * @OA\Property( + * property="color", + * type="string", + * description="icon颜色", + * ), + * @OA\Property( + * property="target", + * type="string", + * description="是否新窗口 _target/_self", + * ), + * @OA\Property( + * property="iframe", + * type="integer", + * description="是否为 iframe模式 0:不是 1:是", + * ), + * @OA\Property( + * property="hide", + * type="integer", + * description="是否显示 0:显示 1:隐藏", + * ), + * @OA\Property( + * property="type", + * type="integer", + * description="在think_auth_rule 表中定义一条规则时,如果type为1, condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100 表示用户的分数在5-100之间时这条规则才会通过。", + * ), + * @OA\Property( + * property="sort", + * type="integer", + * description="排序", + * ), + * @OA\Property( + * property="menu_type", + * type="integer", + * description="菜单类型 0:菜单 1:按钮", + * ), + * @OA\Property( + * property="open_type", + * type="integer", + * description="打开方式 0:组件 1:内链 2:外链", + * ), + * @OA\Property( + * property="is_dev", + * type="integer", + * description="1:开发者模式(即tp调试模式开启)", + * ), + * @OA\Property( + * property="status", + * type="integer", + * description="状态:为1正常,为0禁用", + * ), + * @OA\Property( + * property="condition", + * type="string", + * description="规则表达式,为空表示存在就验证,不为空表示按照条件验证", + * ), + * @OA\Property( + * property="remarks", + * type="string", + * description="备注", + * ), + * @OA\Property( + * property="child_list", + * type="string", + * description="100100 子集菜单集合", + * ), + * @OA\Property( + * property="children", + * type="object", + * description="子集菜单对象", + * ), + * @OA\Property( + * property="delete_time", + * type="integer", + * description="是否删除 null:未删除" + * ), + * @OA\Property( + * property="update_time", + * type="integer", + * description="更新时间" + * ), + * @OA\Property( + * property="create_time", + * type="integer", + * description="创建时间" + * ), + * ) + * ), + * + * ), + */ + +} diff --git a/app/api/model/Message.php b/app/api/model/Message.php new file mode 100644 index 0000000..6048b35 --- /dev/null +++ b/app/api/model/Message.php @@ -0,0 +1,248 @@ +request->param('title', ''); + + $_op = $this->where('is_init',1); + + + //通用逻辑 + return parent::parentLists($_op); + } + + + public function getUserMessage($type){ + $_op = $this->where('receve_id',UID); + + if($type == 99){ + //获取未读系统消息总数 + $gg_count = $this->where('receve_id',UID)->where('type',1)->where('read_status',0)->count(); + //获取未读私信消息总数 + $sx_count = $this->where('receve_id',UID)->where('type',0)->where('read_status',0)->count(); + + //获取通知类型最新5条消息 + $gg_list = $this->where('receve_id',UID)->where('type',1)->where('read_status',0)->order('create_time desc,id desc')->limit(5)->select()->toArray(); + //获取 + $sx_list = $this->where('receve_id',UID)->where('type',0)->where('read_status',0)->order('create_time desc,id desc')->limit(5)->select()->toArray(); + $reData = []; + $reData['gg']['count'] = $gg_count; + $reData['gg']['list'] = $gg_list; + + $reData['sx']['count'] = $sx_count; + $reData['sx']['list'] = $sx_list; + return $reData; + }else if($type != 98){ + $_op->where('type',$type); + } + $_op->order('read_status asc,create_time desc'); + return parent::parentLists($_op); + + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + + //读取数据前格式化数据 + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + + $content_id = $model->getAttr('content_id'); + $receve_id = $model->getAttr('receve_id'); + $send_id = $model->getAttr('send_id'); + if($content_id){ + $c_model = new MessageContent(); + $contents = $c_model->find($content_id); + $model->setAttr('title',$contents['title']); + $model->setAttr('content',$contents['content']); + $model->setAttr('pub_time',$contents['create_time']); + } + $u_model = new Manager(); + + if($receve_id>0){ + $receve_user = $u_model->find($receve_id)->toArray(); + if($receve_user){ + $model->setAttr('receve_user_str',$receve_user['truename']); + }else{ + $model->setAttr('receve_user_str',''); + } + }else{ + $model->setAttr('receve_user_str','所有人'); + } + + if($send_id>0){ + $receve_user = $u_model->find($send_id)->toArray(); + if($receve_user){ + $model->setAttr('send_user_str',$receve_user['truename']); + $model->setAttr('avatar_str',$receve_user['avatar']); + }else{ + $model->setAttr('send_user_str',''); + } + }else{ + $model->setAttr('send_user_str',''); + } + + +// $model->setAttr('create_time_str',date('Y-m-d H:i:s',$model->getAttr('create_time'))); + $model->setAttr('read_status_str',$model->getAttr('create_time')==1?'已读':'未读'); + + //业务逻辑 + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + parent::onBeforeWrite($model); + + //写入数据前格式化数据 + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + + $title = $model->getAttr('title'); + $content = $model->getAttr('content'); + $message_content = new MessageContent(); + $ms_content['title'] = $title; + $ms_content['content'] = $content; + if(request()->action() == 'add'){ + if($title && $content){ + $content_id = $message_content->parentAdd($ms_content); + $model->setAttr('content_id',$content_id); + } + }else{ + $c_id = $model->getAttr('content_id'); + if($title && $content){ + $message_content->where('id',$c_id)->save($ms_content); + } + } + $receve_id = $model->getAttr('receve_id'); + $type = $receve_id<0?1:0; + + $model->setAttr('send_id',UID); + + $model->setAttr('type',$type); + $model->hidden(['title','content']); + + //业务逻辑 + // code... + } + + public static function onAfterDelete(Model $model) + { + parent::onAfterDelete($model); // TODO: Change the autogenerated stub + + } + + public static function onAfterWrite(Model $model) + { + parent::onAfterWrite($model); // TODO: Change the autogenerated stub + + } + + /* + * 查看用户是否有待插入的公告 + * */ + public function setUserMessage($uid=0,$site_id=0){ + if(!$uid){ + $uid = UID; + } + if(!$site_id){ + $site_id = SITE_ID; + } + //获取用户最近一份公告的时间 + $last_gg = $this->withTrashed()->where('receve_id',$uid)->where('type',1)->order('create_time desc')->find(); + + if($last_gg){ + $last_gg = $last_gg->toArray(); + $start_time = strtotime($last_gg['create_time']); + } + //查询所有该用户没有的公告 + $no_ggs = $this->where('site_id','in',[1,$site_id])->where('type',1)->where('create_time','>',$start_time)->order('create_time asc')->select()->toArray(); +//var_dump($no_ggs);die; + foreach ($no_ggs as $item){ + $model = new Message(); + $gg_item = []; + $gg_item['send_id'] = $item['send_id']; + $gg_item['site_id'] = $site_id; + $gg_item['receve_id'] = $uid; + $gg_item['content_id'] = $item['content_id']; + $gg_item['is_init'] = 0; + $gg_item['type'] = 1; + $model->save($gg_item); + } + return true; + + } + + public function setMessageRead($ids,$type){ + $_where = [ + ['site_id', '=', SITE_ID], + ['receve_id','=',UID] + ]; + if($ids == -1){ + if($type==0 || $type==1){ + $_where[] = ['type','=',$type]; + } + }else{ + $ids = explode(',', $ids); + $_where[] = ['id', 'in', $ids]; + } + $affected = self::update(['read_status' => 1], $_where); + return $affected; + } + + + public function setMessageDelete($ids){ + + $ids = explode(',', $ids); + if($ids){ + $_op = $this->where('id', 'in', $ids)->where('receve_id',UID)->where('site_id', SITE_ID)->select(); + if ($_op->isEmpty()) { + return send_http_status('', 101); + } + return self::destroy($ids); //TP软删除 + }else{ + return false; + } + + + } + + +} diff --git a/app/api/model/MessageContent.php b/app/api/model/MessageContent.php new file mode 100644 index 0000000..d2a6f3c --- /dev/null +++ b/app/api/model/MessageContent.php @@ -0,0 +1,79 @@ +request->param('title', ''); + $_op = $this->whereLike('title', "{$dict_name}%"); + + //通用逻辑 + return parent::parentLists($_op); + } + + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + //读取数据前格式化数据 + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + //业务逻辑 + } + + /** + * 新增或修改前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeWrite(Model $model) + { + parent::onBeforeWrite($model); + + //写入数据前格式化数据 + if (!in_array(request()->action(), ['add', 'edit'])) { + return true; + } + //业务逻辑 + // code... + } + + public static function onAfterDelete(Model $model) + { + parent::onAfterDelete($model); // TODO: Change the autogenerated stub + + } + + public static function onAfterWrite(Model $model) + { + parent::onAfterWrite($model); // TODO: Change the autogenerated stub + + } + + +} diff --git a/app/api/model/Site.php b/app/api/model/Site.php new file mode 100644 index 0000000..2794834 --- /dev/null +++ b/app/api/model/Site.php @@ -0,0 +1,31 @@ +whereFindInSet('host', $this->request->host()); + $_op->where('start_time', '<=', time()); + $_op->where('end_time', '>=', time()); + $_data = $_op->find(); + if (empty($_data)) { + return send_http_status('', 102); + } + return $_data->id; + } + +} diff --git a/app/api/model/Token.php b/app/api/model/Token.php new file mode 100644 index 0000000..65e997f --- /dev/null +++ b/app/api/model/Token.php @@ -0,0 +1,72 @@ +token = $data['token']; + $this->refresh_token = $data['refresh_token']; + $this->user_id = $data['user_id']; + $this->start_time = time(); + $this->site_id = $data['site_id']; + $this->end_time = time() + config('jwtauth.web.expire_time_refresh'); + $this->save(); + } + + /** + * 返回刷新token + * @param $refresh_token + * @return array|void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function refreshToken($refresh_token) + { + $time = time(); + $_op = self::where('refresh_token', $refresh_token); + $_op->where('start_time', '<', $time)->where('end_time', '>', $time); + $token_data = $_op->field('user_id,site_id')->find(); + if (!empty($token_data)) { + /** + * 登录用户ID + */ + defined('UID') ?: define('UID', $token_data->getData('user_id')); + + /** + * 站点ID + */ + defined('SITE_ID') ?: define('SITE_ID', $token_data->getData('site_id')); +// //查询用户信息 +// $loginUser = (new Manager())->parentRead(UID)->toArray(); + //发送JWT签证密钥 + $token_data->id = $token_data->user_id; + $token_obj = (JwtAuth::getInstance())->setToken($token_data); + $token = $token_obj->getToken(); + $_data = [ + 'token' => $token, + 'refresh_token' => $refresh_token, //返回refresh_token本身不延期 + 'expire' => config('jwtauth.web.expire_time'), +// 'refresh_token' => $token_obj->createRefreshToken($this), + ]; + return $_data; + } + return; + } + +} diff --git a/app/api/model/UploadLog.php b/app/api/model/UploadLog.php new file mode 100644 index 0000000..4de22a8 --- /dev/null +++ b/app/api/model/UploadLog.php @@ -0,0 +1,164 @@ +request->param('current', '/'); + + $direct = $this->request->param('direct', 0); + + $dic_arr = [ + 'host', + 'y', + 'date', +// 'd', + 'url', + ]; + + $current_arr = explode('/',$current); + + if($current == '/' || $current == ''){ + $current_arr = [ + '/' + ]; + } + $current_arr[0] = '/'; + //向下层取数据 + $field_name = $dic_arr[count($current_arr)-1]; + + + if($field_name == 'url'){ + $field_str = $field_name.',name,type,size ,0 as isDirectory,update_time,id'; + }else{ + $field_str = $field_name.' as name ,1 as isDirectory'; + } + + $conition = []; + + + $current_arr_1 = $current_arr; + if(count($current_arr_1)>1){ + unset($current_arr_1[count($current_arr_1)-1]); + $current_arr_1[0] = ''; + } + $pre_current = implode('/',$current_arr_1); + + $_op = $this->field($field_str); + //获取该id下的所有的域名 + $siteModel = new Site(); + $hosts_str = $siteModel->where('id',SITE_ID)->find(); + $host_arr = []; + if($current == '' || $current == '/'){ + if($hosts_str['host']){ + $host_arr = explode(',',$hosts_str['host']); + } + if(count($host_arr)==0){ + $host_arr[] = $_SERVER['host']; + } + $_op->whereIn('host',$host_arr); + }else{ + $conition['host'] = $current_arr[1]; + } + + //年 + if(isset($current_arr[2]) && $current_arr[2]!=''){ + $conition['Y'] = $current_arr[2]; + } +// //月 +// if(isset($current_arr[3]) &&$current_arr[3]!='' ){ +// $conition['m'] = $current_arr[3]; +// } +// //日 +// if(isset($current_arr[4]) && $current_arr[4]!=''){ +// $conition['d'] = $current_arr[4]; +// } + //日期 + if(isset($current_arr[3]) && $current_arr[3]!=''){ + $conition['date'] = $current_arr[3]; + } + + $_op->distinct(true); + $_op->where($conition); + + $data = parent::parentLists(!empty($_op) ? $_op : '',1,0); + $count = $this->field($field_name)->where($conition)->group($field_name)->count(); + + $data['page_total'] = $count; + $returnData = []; +// $data['current'] = $current; +// $data['pre_current'] = $pre_current; +// $returnData['data'] = $data; +// $returnData['page_total'] = $data['page_total']; + + return $data; + } + + public function getLists(){ + //执行通用查询 + return parent::parentLists(); + } + + + public function getStorgaeImagesLists(){ + $type = $this->request->param('type', 0); + $_op = $this; + if($type){ + $_op->where('type',$type); + } + + + //执行通用查询 + return parent::parentLists(); + } + + /** + * 读取后 + * @param Model $model + * @return bool|void + */ + public static function onAfterRead(Model $model) + { + if (in_array(request()->action(), ['edit', 'delete', 'status'])) { + return true; + } + $model->offsetSet('action_ip', long2ip((int)$model->getAttr('action_ip'))); + } + + /** + * 新增前 + * @param Model $model + * @return mixed|void + */ + public static function onBeforeInsert(Model $model) + { + if (!in_array(request()->action(), ['add'])) { + return true; + } + //密码加密 + $m = $model->getAttr('m'); + $d = $model->getAttr('d'); + $model->setAttr('date', $m.$d); + } + + +} diff --git a/app/api/route/app.php b/app/api/route/app.php new file mode 100644 index 0000000..5db043d --- /dev/null +++ b/app/api/route/app.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +use think\facade\Route; + +//Route::rule(':ver/login', 'api/v1.Login/login')->allowCrossDomain(); +//Route::rule(':ver/refreshToken', 'api/v1.Login/refreshToken'); +//Route::rule(':ver/getManager/:id', 'api/v1.Manager/read'); +//Route::rule(':ver/getUser', 'api/v1.Manager/index')->allowCrossDomain(['Access-Control-Allow-Headers'=>'token']); +//Route::rule(':ver/updateManager/[:id]', 'api/:ver.Manager/edit'); +//Route::rule(':ver/addManager', 'api/:ver.Manager/create'); +//Route::rule(':ver/delManager', 'api/:ver.Manager/delete'); +//Route::rule(':ver/index', 'api/v1.Index/index'); + +//Route::rule('getManager', 'api/Manager/index'); +Route::get('manager/int ', 'api/Manager/read'); +Route::rule(':ver/test', 'api/:ver.Test/index'); //测试多层级api \ No newline at end of file diff --git a/app/api/validate/Ad.php b/app/api/validate/Ad.php new file mode 100644 index 0000000..d541ba3 --- /dev/null +++ b/app/api/validate/Ad.php @@ -0,0 +1,58 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'name|广告名称' => "require|length:1,50", + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'name.require' => '请输入广告位名称', + 'name.length' => '广告位名称必需在1~50位之间', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('name', 'require'); + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + + } +} diff --git a/app/api/validate/AdData.php b/app/api/validate/AdData.php new file mode 100644 index 0000000..b1bce51 --- /dev/null +++ b/app/api/validate/AdData.php @@ -0,0 +1,68 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'adid' => "require|number|length:1,50", + 'link' => 'url', + 'sort' => 'number', + 'start_time' => 'require|dateFormat:Y-m-d H:i:s', + 'end_time' => 'require|dateFormat:Y-m-d H:i:s|gt:start_time', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'adid.require' => '请输入广告位ID', + 'adid.number' => '广告位ID必须是数字', + 'adid.length' => '广告位ID需在1~50位之间', + 'start_time.dateFormat' => '开始时间格式不正确', + 'end_time.dateFormat' => '结束时间格式不正确', + 'end_time.gt' => '结束时间必须大于开始时间', + 'end_time.require' => '结束时间不能为空', + 'start_time.require' => '结束时间不能为空', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('name', 'require'); + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + + } +} diff --git a/app/api/validate/Block.php b/app/api/validate/Block.php new file mode 100644 index 0000000..906a064 --- /dev/null +++ b/app/api/validate/Block.php @@ -0,0 +1,59 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'ident|碎片标识' => "require|uniqueSite:cloud_block|alphaDash|length:1,50", + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'ident.require' => '请输入碎片标识', + 'ident.length' => '碎片标识必需在1~50位之间', + 'ident.alphaDash'=>'碎片标志是能是数字字母和下划线组成' + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('ident', 'require'); + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + + } +} diff --git a/app/api/validate/Config.php b/app/api/validate/Config.php new file mode 100644 index 0000000..1a00a23 --- /dev/null +++ b/app/api/validate/Config.php @@ -0,0 +1,54 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'name|配置名称' => "require|uniqueSite:bmz_config|length:1,30", + 'value|配置内容' => 'require|length:1,200', + 'type|配置类型' => 'require|length:1,30', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'name.require' => '请输入配置名称', + 'name.length' => '配置名称必需在1~30位之间', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + return $this->only(['name', 'value', 'type']) + ->remove('name', 'uniqueSite|require|length') + ->remove('value', 'length|require') + ->remove('type', 'length|require'); + } +} diff --git a/app/api/validate/Demo.php b/app/api/validate/Demo.php new file mode 100644 index 0000000..76f7596 --- /dev/null +++ b/app/api/validate/Demo.php @@ -0,0 +1,67 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'username|测试账号' => "require|uniqueSite:system_demo|length:4,20", + 'truename|测试真实姓名' => 'require|length:1,20', + 'password|测试密码' => 'require|length:6,20', + 'sex|性别' => 'number|between:0,2', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'username.require' => '请输入测试账号', + 'username.length' => '测试账号名称必需在4~20位之间', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('username', 'require') + ->remove('truename', 'require') + ->remove('phone', 'require'); + //需删除以上代码,重新定义 + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + return $this->only(['username', 'truename', 'sex']) + ->remove('username', 'uniqueSite|require|length') + ->append('username', 'min:1|max:20') + ->remove('truename', 'uniqueSite|require'); + //需删除以上代码,重新定义 + } +} diff --git a/app/api/validate/Dictionary.php b/app/api/validate/Dictionary.php new file mode 100644 index 0000000..3b43ab4 --- /dev/null +++ b/app/api/validate/Dictionary.php @@ -0,0 +1,80 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'dict_type_id|字典类型id' => "require|integer", + 'dict_name|字典名称' => 'require|length:1,100', + 'dict_value|字典值' => "require|checkValue", + + ]; + + // 验证名称是否存在 + protected function checkValue($value, $rule, $data=[]) + { + $_op = new \app\api\model\Dictionary(); + $count = $_op->chekName($value); + return $count == 0 ? true : '该类型下字典值不能重复'; + } + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'dict_name.require' => '请输入字典标志', + 'dict_name.length' => '字典标志长度为1~100', + 'dict_value.require' => '请输入字典值', + 'dict_value.length' => '字典值为1~100', + 'dict_type_id.require' => '字典类型不能为空', + 'dict_type_id.integer' => '字典类型为存数字', + + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { +// return $this->remove('dict_symbol', 'dict_value')->append(''); + + } + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + //需删除以上代码,重新定义 + return $this->only(['dict_type_id','dict_name', 'dict_value']) + ->remove('dict_name', 'require') + ->remove('dict_value', 'require') + ->remove('dict_value', 'checkValue') +// ->remove('dict_type_id', 'require|integer') + ; + } + +} diff --git a/app/api/validate/DictionaryType.php b/app/api/validate/DictionaryType.php new file mode 100644 index 0000000..c63cfce --- /dev/null +++ b/app/api/validate/DictionaryType.php @@ -0,0 +1,64 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'dict_symbol|字典标志' => "require|checkSymbol|alphaDash|length:2,50", + 'dict_name|字典名称' => 'require|length:2,100', + ]; + + // 验证名称是否存在 + protected function checkSymbol($value, $rule, $data=[]) + { + $_op = new \app\api\model\DictionaryType(); + $count = $_op->where('dict_symbol',$value)->count(); + return $count == 0 ? true : '字典类型标志不能重复'; + } + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'dict_symbol.require' => '请输入字典类型标志', + 'dict_symbol.length' => '字典类型标志必需在2~50位之间', + 'dict_symbol.checkSymbol' => '字典类型标志不能重复', + 'dict_symbol.alphaDash' => '字典类型只能是字母,数字和下划线', + 'dict_name.require' => '请输入字典类型名称', + 'dict_name.length' => '字典类型名称必需在2~100位之间', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('dict_symbol', 'checkSymbol'); + //需删除以上代码,重新定义 + } + + +} diff --git a/app/api/validate/Group.php b/app/api/validate/Group.php new file mode 100644 index 0000000..a0d65d9 --- /dev/null +++ b/app/api/validate/Group.php @@ -0,0 +1,52 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'title|角色名称' => "require|uniqueSite:system_auth_group|length:2,20", + 'role_code|角色标识' => "require|uniqueSite:system_auth_group|length:2,20", + 'remark|角色备注' => 'length:2,200', + 'status|状态' => 'number|length:1', + 'page|页码' => 'number|length:1,10', + 'pagesize|每页数量' => 'number|length:1,10', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'title.require' => '请输入角色名称', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + public function sceneLists() + { + return $this->remove('title', 'require|uniqueSite|length') + ->append('title', 'min:1|max:20') //删除title length之后 ,再append length就无效了,只能换成min max + ->remove('role_code','require|uniqueSite|length') + ->append('role_code','min:1|max:20'); + } + +} diff --git a/app/api/validate/Log.php b/app/api/validate/Log.php new file mode 100644 index 0000000..e3fb71b --- /dev/null +++ b/app/api/validate/Log.php @@ -0,0 +1,62 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'username|管理员账号' => "length:1,20", + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'username.length' => '管理员账号名称必需在1~20位之间', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('username', 'require') + ->remove('truename', 'require') + ->remove('phone', 'require'); + //需删除以上代码,重新定义 + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + return $this->only(['username', 'truename', 'sex']) + ->remove('username', 'uniqueSite|require|length') + ->append('username', 'min:1|max:20') + ->remove('truename', 'uniqueSite|require'); + //需删除以上代码,重新定义 + } +} diff --git a/app/api/validate/Manager.php b/app/api/validate/Manager.php new file mode 100644 index 0000000..41ffef1 --- /dev/null +++ b/app/api/validate/Manager.php @@ -0,0 +1,92 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'username|管理员账号' => "require|uniqueSite:system_user|length:4,20", + 'truename|管理员真实姓名' => 'require|length:1,20', + 'password|管理员密码' => 'require|length:6,20', + 'sex|性别' => 'number|length:1,1', + 'phone|手机号' => 'require|uniqueSite:system_user|mobile', + 'email|邮箱' => 'email|uniqueSite:system_user', + 'avatar|头像' => 'length:6,255', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'username.require' => '请输入管理员账号', + 'username.length' => '管理员账号名称必需在4~20位之间', + 'password2.confirm' => '新密码与确认密码不一致', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + return $this->remove('username', 'require') + ->remove('password', 'require'); + } + + /** + * 修改密码场景验证 + * @return Manager + */ + public function sceneUpdatePassword() + { + return $this->remove('username', 'require') + ->remove('truename', 'require') + ->remove('phone', 'require') + ->append('password2|确认密码', 'require|confirm:password'); + } + + /** + * 重置密码场景验证 + * @return Manager + */ + public function sceneResetPassword() + { + return $this->remove('username', 'require') + ->remove('truename', 'require') + ->remove('phone', 'require'); + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + return $this->only(['username', 'truename', 'sex']) + ->remove('username', 'uniqueSite|require|length') + ->append('username', 'min:1|max:20') + ->remove('truename', 'require'); + } +} diff --git a/app/api/validate/Menu.php b/app/api/validate/Menu.php new file mode 100644 index 0000000..0d5ac40 --- /dev/null +++ b/app/api/validate/Menu.php @@ -0,0 +1,68 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + 'name|菜单名称' => "require|uniqueSite:system_auth_rule|length:1,15", + 'path|路由地址' => 'length:1,100', + 'model_name|接口地址' => 'require|length:1,100', + 'component|组件路径' => 'length:1,100', + 'authority|权限标识' => 'uniqueSite:system_auth_rule|length:1,100', + 'icon|图标' => 'length:1,100', + 'hide|显示隐藏' => 'max:1|min:0', + 'sort|排序' => 'between:0,10000', + 'menu_type|菜单类型' => 'max:1|min:0', + 'remarks|菜单备注' => 'length:1,100', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'username.require' => '请输入菜单名称', + 'username.length' => '菜单名称必需在1~15位之间', + ]; + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { + + } + + /** + * 自定义查询场景验证 可灵活设置验证字段 + * @return Manager + */ + public function sceneLists() + { + return $this->remove('name', 'require|uniqueSite') + ->remove('model_name', 'require'); + } +} diff --git a/app/api/validate/Message.php b/app/api/validate/Message.php new file mode 100644 index 0000000..1669793 --- /dev/null +++ b/app/api/validate/Message.php @@ -0,0 +1,42 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'id' => 'number|length:1,10', + ]; + + + + + /** + * 验证场景规则 + * @var array + */ + protected $scene = [ + 'read' => ['id'], + ]; + + /** + * 修改场景验证 + * @return Manager + */ + public function sceneUpdate() + { +// return $this->remove('dict_symbol', 'dict_value')->append(''); + + } + + +} diff --git a/app/common.php b/app/common.php new file mode 100644 index 0000000..7960bfd --- /dev/null +++ b/app/common.php @@ -0,0 +1,251 @@ +where('site_id', SITE_ID); + //id存在需要判断修改逻辑下排除id本身且不为空的情况下 有没有重复 + $id = !empty($data['id']) ? $data['id'] : ''; + if ($id) { + $_db->where('id', '<>', $id); + $_db->where($name, '<>', ' '); + } + $_data = $_db->where($name, $value)->find(); + if (empty($_data)) { + return true; + } + return $note . '已存在,请重新输入!!!'; +} + +/** + * json统一返回 + * @param type $code 状态编码 0正常 + * @param type $data 返回数据 + * @param type $msg 错误的提示信息 + * @return type + */ +function send_http_status($data = [], $code = 0, $httpStatus = 200, $msg = '') +{ + $msg = !empty($msg) ? $msg : config('dictionary')[$code]; + //排除成功的状态信息 + if (in_array($code, [201, 203, 205, 207, 209])) { + $code = 0; + } + + $_data = [ + 'code' => $code, + 'msg' => $msg, + 'count' => 0, + 'data' => $data, + ]; + + //更改data2级数据为一级 增加列表页count数据 + if (isset($data['page_total'])) { + $_data['count'] = $data['page_total']; + $_data['data'] = empty($data['data']) ? [] : $data['data']; + } + + if ($code != 0 && $code != 200) { + throw new \app\exception\HttpException($httpStatus, $msg, $code); + } + return json($_data, $httpStatus); +} + +/** + * 把格式数组,拆分父栏目,与子栏目 + * @param type $arr 一级数组 + * @param type $pid 父id + * @param type $child_name 生成子数组的名称 + * @param type $primary 表的主键值,上一级id + * @return type + */ +function get_tree(array $arr, $pid = 0, $child_name = 'children', $primary = 'id') +{ + if (empty($arr)) { + return $arr; + } + if (is_array($arr)) { + $child = get_child($arr, $pid); + if (empty($child)) { + return null; + } + foreach ($child as $key => $value) { + $current_child = get_tree($arr, $value[$primary]); + if ($current_child != null) { + $child[$key][$child_name] = $current_child; + } + } + } + return $child; +} + +//根据pid获取子栏目 返回子栏目数组集合 +function get_child($arr, $pid = 0) +{ + if (is_array($arr)) { + $child = array(); + foreach ($arr as $key => $value) { + if ($value['parent_id'] == $pid) { + $child[$key] = $value; + } + } + return array_values($child); + } + return $arr; +} + + +/** + * 云掌图片服务器裁剪函数 + * @param $src 源地址,不带?号 + * @param $w 宽度 + * @param $h 高度 + * @param $g 裁剪位置 nw西北 north正北 ne东北 west正西 center中间 east正东 sw西南 south正南 se东南 + * @param string $type 执行方式 crop:裁剪 resize:缩放 + */ +function crop_thumb($src, $w = '', $h = '') +{ + //判断图片是不是全路径 + $cdn_img_host = strpos($src, 'http') !== 0 ? config('cdn_img_host') : ''; + if (empty($src) || !$w || !$h) { + return $cdn_img_host . $src; + } + //限制宽、高大小 + if ($w > 2000 || $w < 5 || $h > 2000 || $h < 5) { + return $src; + } + //有逗号分割符取第一张图片 + $first_pic = strstr($src, ',', true); + $src = !empty($first_pic) ? $first_pic : $src; + //拆分图片后缀 + $last_pos = strripos($src, '.'); //图片格式中点的位置 + $base_url = mb_substr($src, 0, $last_pos); //去图片后缀的url + $exp_url = mb_substr($src, $last_pos); //图片后缀 + return $cdn_img_host . $base_url . '_' . $w . 'x' . $h . $exp_url; +} + + +/** + * 判断浏览器名称和版本 + */ +function get_client_browser() +{ + $agent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : false; + if (!$agent) { + return false; + } + if ((strpos($agent, 'MSIE') == false) && (strpos($agent, 'Trident') !== FALSE)) { + return 'Internet Explorer 11.0'; + } + if (strpos($agent, 'MSIE 10.0') != false) { + return 'Internet Explorer 10.0'; + } + if (strpos($agent, 'MSIE 9.0') != false) { + return 'Internet Explorer 9.0'; + } + if (strpos($agent, 'MSIE 8.0') != false) { + return 'Internet Explorer 8.0'; + } + if (strpos($agent, 'MSIE 7.0') != false) { + return 'Internet Explorer 7.0'; + } + if (strpos($agent, 'MSIE 6.0') != false) { + return 'Internet Explorer 6.0'; + } + if (strpos($agent, 'Edge') != false) { + return 'Edge'; + } + if (strpos($agent, '360SE') != false) { + return '360SE'; + } + if (strpos($agent, 'QQBrowser') != false) { + return 'QQBrowser'; + } + if (strpos($agent, 'Firefox') != false) { + return 'Firefox'; + } + if (strpos($agent, 'Chrome') != false) { + return 'Chrome'; + } + if (strpos($agent, 'Safari') != false) { + return 'Safari'; + } + if (strpos($agent, 'Opera') != false) { + return 'Opera'; + } + //微信浏览器 + if (strpos($agent, 'MicroMessage') != false) { + return '微信浏览器'; + } +} + +/** + * 获取系统类型 + */ +function get_client_os() +{ + + $agent = strtolower($_SERVER['HTTP_USER_AGENT']); + + if (strpos($agent, 'windows nt')) { + $platform = 'Windows'; + } elseif (strpos($agent, 'macintosh')) { + $platform = 'Mac'; + } elseif (strpos($agent, 'ipod')) { + $platform = 'iPod'; + } elseif (strpos($agent, 'ipad')) { + $platform = 'iPad'; + } elseif (strpos($agent, 'iphone')) { + $platform = 'iPhone'; + } elseif (strpos($agent, 'android')) { + $platform = 'Android'; + } elseif (strpos($agent, 'unix')) { + $platform = 'Unix'; + } elseif (strpos($agent, 'linux')) { + $platform = 'Linux'; + } else { + $platform = '其他'; + } + + return $platform; +} + +//过滤单双引号 +function htmlspecialchars_custom($str) +{ + return htmlspecialchars($str, ENT_QUOTES); +} \ No newline at end of file diff --git a/app/event.php b/app/event.php new file mode 100644 index 0000000..e9851bb --- /dev/null +++ b/app/event.php @@ -0,0 +1,17 @@ + [ + ], + + 'listen' => [ + 'AppInit' => [], + 'HttpRun' => [], + 'HttpEnd' => [], + 'LogLevel' => [], + 'LogWrite' => [], + ], + + 'subscribe' => [ + ], +]; diff --git a/app/exception/ExceptionHandle.php b/app/exception/ExceptionHandle.php new file mode 100644 index 0000000..f4f43a2 --- /dev/null +++ b/app/exception/ExceptionHandle.php @@ -0,0 +1,82 @@ +msg = $e->getMessage() ?: $this->msg; + //兼容Exception类 这个类没有getStatusCode方法 + if ($e instanceof HttpException) { + $this->httpStatusCode = $e->getStatusCode() ?: $this->httpStatusCode; + } + $this->code = $e->getCode() ?: $this->code; + $data = [ + 'msg' => $this->msg, + 'data' => [], + 'code' => $this->code, + ]; + return json($data, $this->httpStatusCode); + + } +} diff --git a/app/exception/HttpException.php b/app/exception/HttpException.php new file mode 100644 index 0000000..e92d872 --- /dev/null +++ b/app/exception/HttpException.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- +declare (strict_types=1); + +namespace app\exception; + +use Exception; + +/** + * HTTP异常 + */ +class HttpException extends \RuntimeException +{ + private $statusCode; + private $headers; + + public function __construct(int $statusCode, string $message = '', $code = 0, array $headers = [], Exception $previous = null) + { + $this->statusCode = $statusCode; + $this->headers = $headers; + + parent::__construct($message, $code, $previous); + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getHeaders() + { + return $this->headers; + } +} diff --git a/app/facade/Test.php b/app/facade/Test.php new file mode 100644 index 0000000..da3fa53 --- /dev/null +++ b/app/facade/Test.php @@ -0,0 +1,19 @@ +find(7)); + } + + public function index(){ + echo "hello"; + } + +} diff --git a/app/index/controller/Jwt.php b/app/index/controller/Jwt.php new file mode 100644 index 0000000..cd36ce7 --- /dev/null +++ b/app/index/controller/Jwt.php @@ -0,0 +1,89 @@ +config = Configuration::forSymmetricSigner( + new Sha256(), + InMemory::plainText($key) + ); + + } + + public function createToken() + { + + +// halt($key,$configuration); +// echo($config instanceof Configuration); + $now = new \DateTimeImmutable(); + $token = $this->config->builder() + // jwt签发者 + ->issuedBy('http://example.com') + // Configures the audience (aud claim) + ->permittedFor('http://example.org') + // jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 + ->identifiedBy('4f1g23a12aa') + // jwt的签发时间 + ->issuedAt($now) + // Configures the time that the token can be used (nbf claim) + ->canOnlyBeUsedAfter($now->modify('+1 minute')) + // Configures the expiration time of the token (exp claim) + ->expiresAt($now->modify('+1 minute')) + // Configures a new claim, called "uid" + ->withClaim('uid', 10) + // Configures a new header, called "foo" + ->withHeader('foo', 'bar') + // Builds a new token + ->getToken($this->config->signer(), $this->config->signingKey()); + +// halt($token->claims()->get('uid')); + halt($token->toString()); + } + + public function parseToken() + { + $_token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhaGJtei5jb20iLCJhdWQiOiJiYXNlLmFoYm16LmNvbSIsImp0aSI6Imx3RWluZ2U0RXk4RXhqeHBVODNrbEtsZmVpYW84TCIsImlhdCI6IjE2MTUyNjkxNDIuOTQ2NDc3IiwibmJmIjoiMTYxNTI2OTE0Mi45NDY0NzciLCJleHAiOiIxNjE1MzQxMTQyLjk0NjQ3NyIsInVzZXJfaWQiOjEsInNpdGVfaWQiOjF9.9bGtgvWH0iredbmuJrfhA2hY0aatEnpX3g-jKY9bLXs'; + $token = $this->config->parser()->parse($_token); + halt($token->headers(), $token->claims()); + } + + public function checkToken() + { + $config = $this->config; + $_token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImZvbyI6ImJhciJ9.eyJpc3MiOiJodHRwOi8vZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwOi8vZXhhbXBsZS5vcmciLCJqdGkiOiI0ZjFnMjNhMTJhYSIsImlhdCI6IjE2MTQxNzc0NjkuNDM3NDMwIiwibmJmIjoiMTYxNDE3NzUyOS40Mzc0MzAiLCJleHAiOiIxNjE0MTc3NTI5LjQzNzQzMCIsInVpZCI6MTB9.oC37eJvIsGGNu7X6J7axbH7-Rzs-MJQsI5gvqmpDn-Q'; + $token = $config->parser()->parse($_token); +// halt($config->parser()->parse($_token)); + $constraint = new Constraint\SignedWith($this->config->signer(), $this->config->verificationKey()); + $config->setValidationConstraints($constraint); + $constraints = $config->validationConstraints(); + if (!$config->validator()->validate($token, ...$constraints)) { + exit('no way!'); + }else{ + $now = new \DateTimeImmutable(); +// $clock = new FrozenClock($now); +// $a = new Constraint\LooseValidAt($clock,new \DateInterval('P2W')); +// halt($a,$token->isExpired($now)); + halt($token); + halt($token->isMinimumTimeBefore($now),$token->isPermittedFor('http://example.org'),$token->isExpired($now),$token->hasBeenIssuedBy('http://example.com')); + exit('验证通过'); + } + } + +} diff --git a/app/index/model/User.php b/app/index/model/User.php new file mode 100644 index 0000000..8350b0c --- /dev/null +++ b/app/index/model/User.php @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- +use think\facade\Route; + +Route::get('user/:id','index/Index/hello'); \ No newline at end of file diff --git a/app/middleware.php b/app/middleware.php new file mode 100644 index 0000000..b135a05 --- /dev/null +++ b/app/middleware.php @@ -0,0 +1,20 @@ + 'Token,Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With'] +// ] +// ], +]; diff --git a/app/middleware/AllowCrossDomain.php b/app/middleware/AllowCrossDomain.php new file mode 100644 index 0000000..9b41b41 --- /dev/null +++ b/app/middleware/AllowCrossDomain.php @@ -0,0 +1,44 @@ +app = $app; + } + + /** + * 处理请求 + * + * @param \think\Request $request + * @param \Closure $next + * @return Response + */ + public function handle($request, \Closure $next) + { + + /** @var Response $response */ + $response = $next($request); + + // 支持跨域请求的host数组['a.cn', 'b.cn'] + $corsHost = ['api.base.ahbmz.com', 'api.ahbmz.com', 'cms.ahbmz.com']; + +// if (!empty($corsHost) && is_array($corsHost) && in_array($request->host(), $corsHost)) { + $response->header([ + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Max-Age' => 1800, + 'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE', + 'Access-Control-Allow-Headers' => 'Authorization, Token, Content-Type, If-Match,If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With', + ]); +// } + return $response; + } +} diff --git a/app/middleware/CheckJwtAuth.php b/app/middleware/CheckJwtAuth.php new file mode 100644 index 0000000..4a11933 --- /dev/null +++ b/app/middleware/CheckJwtAuth.php @@ -0,0 +1,70 @@ +root()) . '/' . $request->pathinfo(); +// $filter_router = config('jwtauth.filter_router'); +// foreach ($filter_router as $v) { +// if (strpos(strtolower($url), strtolower($v)) === 0) { +// return $next($request); +// } +// } + + //测试开户app_debug,设置自定用户参数及站点参数 + env('app_debug') ? $this->getLocalToken() : $this->getJwtToken(); + + return $next($request); + } + + /** + * 正式环境 + * @return \type + */ + private function getJwtToken() + { + $token = JwtAuth::getInstance()->checkToken(request()->header('Token')); + if ($token) { + + /** + * 登录用户ID + */ + defined('UID') ?: define('UID', $token['user_id']); + + /** + * 站点ID + */ + defined('SITE_ID') ?: define('SITE_ID', $token['site_id']); + } + } + + /** + * 本地模拟 + */ + private function getLocalToken() + { + /** + * 登录用户ID + */ + defined('UID') ?: define('UID', Request()->param('uid', 1)); + + /** + * 站点ID + */ + defined('SITE_ID') ?: define('SITE_ID', Request()->param('site_id', 1)); + } +} diff --git a/app/provider.php b/app/provider.php new file mode 100644 index 0000000..081d2dc --- /dev/null +++ b/app/provider.php @@ -0,0 +1,11 @@ + Request::class, +// 'think\exception\Handle' => ExceptionHandle::class, //原系统 + 'think\exception\Handle' => 'app\\exception\\ExceptionHandle', //自定义异常类 +]; diff --git a/app/service.php b/app/service.php new file mode 100644 index 0000000..db1ee6a --- /dev/null +++ b/app/service.php @@ -0,0 +1,9 @@ +=7.1.0", + "topthink/framework": "^6.0.0", + "topthink/think-orm": "^2.0", + "topthink/think-multi-app": "^1.0", + "lcobucci/jwt": "^4.0", + "topthink/think-migration": "^3.0", + "doctrine/annotations": "^1.8", + "zircote/swagger-php": "^3.1", + "swiftmailer/swiftmailer": "^6.2", + "edward1108/edward-captcha": "^1.1" + }, + "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" + ] + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..ef1e18a --- /dev/null +++ b/composer.lock @@ -0,0 +1,2350 @@ +{ + "_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": "453dca43ded142a762a3493488900707", + "packages": [ + { + "name": "doctrine/annotations", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/b17c5014ef81d212ac539f07a1001832df1b6d3b", + "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^9.1.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.12.1" + }, + "time": "2021-02-21T21:00:45+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2020-05-25T17:44:05+00:00" + }, + { + "name": "edward1108/edward-captcha", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/Edward1108/edward-captcha.git", + "reference": "426ceee34507c30d4b21e0dd349b571371aef700" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Edward1108/edward-captcha/zipball/426ceee34507c30d4b21e0dd349b571371aef700", + "reference": "426ceee34507c30d4b21e0dd349b571371aef700", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1.0", + "topthink/framework": "^6.0.0" + }, + "type": "library", + "extra": { + "think": { + "services": [ + "edward\\captcha\\CaptchaService" + ], + "config": { + "captcha": "src/config.php" + } + } + }, + "autoload": { + "psr-4": { + "edward\\captcha\\": "src/" + }, + "files": [ + "src/helper.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "edward", + "email": "xuyuanhua1987@163.com" + } + ], + "description": "ThinkPHP6验证码(图片、短信)支持api友好化", + "homepage": "https://github.com/Edward1108/edward-captcha", + "support": { + "issues": "https://github.com/Edward1108/edward-captcha/issues", + "source": "https://github.com/Edward1108/edward-captcha/tree/master" + }, + "time": "2020-07-22T06:49:35+00:00" + }, + { + "name": "egulias/email-validator", + "version": "2.1.25", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/lexer": "^1.0.1", + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.10" + }, + "require-dev": { + "dominicsayers/isemail": "^3.0.7", + "phpunit/phpunit": "^4.8.36|^7.5.15", + "satooshi/php-coveralls": "^1.0.1" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/2.1.25" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2020-12-29T14:50:06+00:00" + }, + { + "name": "lcobucci/clock", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3", + "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "infection/infection": "^0.17", + "lcobucci/coding-standard": "^6.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-deprecation-rules": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/php-code-coverage": "9.1.4", + "phpunit/phpunit": "9.3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/2.0.x" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2020-08-27T18:56:02+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "4.1.2", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "c544710aa18e079baf0027ca4c8236913f46945b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c544710aa18e079baf0027ca4c8236913f46945b", + "reference": "c544710aa18e079baf0027ca4c8236913f46945b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-sodium": "*", + "lcobucci/clock": "^2.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "infection/infection": "^0.21", + "lcobucci/coding-standard": "^6.0", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.0@alpha", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-deprecation-rules": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/php-invoker": "^3.1", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/4.1.2" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2021-02-19T19:37:15+00:00" + }, + { + "name": "league/flysystem", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "9be3b16c877d477357c015cec057548cf9b2a14a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a", + "reference": "9be3b16c877d477357c015cec057548cf9b2a14a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "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-fileinfo": "Required for MimeType", + "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.x" + }, + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2020-08-23T07:39:11+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": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "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.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", + "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.18", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" + }, + "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.7.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2021-01-18T20:58:21+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": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "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.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://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/master" + }, + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "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": "http://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.3" + }, + "time": "2020-03-23T09:12:05+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": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "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": "swiftmailer/swiftmailer", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/698a6a9f54d7eb321274de3ad19863802c879fb7", + "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "egulias/email-validator": "^2.0", + "php": ">=7.0.0", + "symfony/polyfill-iconv": "^1.0", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "symfony/phpunit-bridge": "^4.4|^5.0" + }, + "suggest": { + "ext-intl": "Needed to support internationalized email addresses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "https://swiftmailer.symfony.com", + "keywords": [ + "email", + "mail", + "mailer" + ], + "support": { + "issues": "https://github.com/swiftmailer/swiftmailer/issues", + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.5" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", + "type": "tidelift" + } + ], + "time": "2021-01-12T09:35:59+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", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/master" + }, + "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": "2020-09-07T11:33:47+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "0d639a0943822626290d169965804f79400e6a04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.2.4" + }, + "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": "2021-02-15T18:55:04+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-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" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, + "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": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "06fb361659649bcfd6a208a0f1fcaf4e827ad342" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/06fb361659649bcfd6a208a0f1fcaf4e827ad342", + "reference": "06fb361659649bcfd6a208a0f1fcaf4e827ad342", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + }, + "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 Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.1" + }, + "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": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" + }, + "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": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-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" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, + "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": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-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" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, + "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": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "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 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.22.1" + }, + "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": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/yaml", + "version": "v5.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "7d6ae0cce3c33965af681a4355f1c4de326ed277" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/7d6ae0cce3c33965af681a4355f1c4de326ed277", + "reference": "7d6ae0cce3c33965af681a4355f1c4de326ed277", + "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-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<4.4" + }, + "require-dev": { + "symfony/console": "^4.4|^5.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v5.2.4" + }, + "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": "2021-02-22T15:48:39+00:00" + }, + { + "name": "topthink/framework", + "version": "v6.0.7", + "source": { + "type": "git", + "url": "https://github.com/top-think/framework.git", + "reference": "db8fe22520a9660dd5e4c87e304034ac49e39270" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/framework/zipball/db8fe22520a9660dd5e4c87e304034ac49e39270", + "reference": "db8fe22520a9660dd5e4c87e304034ac49e39270", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "league/flysystem": "^1.0", + "league/flysystem-cached-adapter": "^1.0", + "php": ">=7.1.0", + "psr/container": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0", + "topthink/think-helper": "^3.1.1", + "topthink/think-orm": "^2.0" + }, + "require-dev": { + "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.0.7" + }, + "time": "2021-01-25T14:48:29+00:00" + }, + { + "name": "topthink/think-helper", + "version": "v3.1.4", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-helper.git", + "reference": "c28d37743bda4a0455286ca85b17b5791d626e10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-helper/zipball/c28d37743bda4a0455286ca85b17b5791d626e10", + "reference": "c28d37743bda4a0455286ca85b17b5791d626e10", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "think\\": "src" + }, + "files": [ + "src/helper.php" + ] + }, + "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/3.0" + }, + "time": "2019-11-08T08:01:10+00:00" + }, + { + "name": "topthink/think-migration", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-migration.git", + "reference": "5717d9e5f3ea745f6dbfd1e30b4402aaadff9a79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-migration/zipball/5717d9e5f3ea745f6dbfd1e30b4402aaadff9a79", + "reference": "5717d9e5f3ea745f6dbfd1e30b4402aaadff9a79", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "topthink/framework": "^6.0.0", + "topthink/think-helper": "^3.0.3" + }, + "require-dev": { + "fzaninotto/faker": "^1.8" + }, + "suggest": { + "fzaninotto/faker": "Required to use the factory builder (^1.8)." + }, + "type": "library", + "extra": { + "think": { + "services": [ + "think\\migration\\Service" + ] + } + }, + "autoload": { + "psr-4": { + "Phinx\\": "phinx/src/Phinx", + "think\\migration\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "support": { + "issues": "https://github.com/top-think/think-migration/issues", + "source": "https://github.com/top-think/think-migration/tree/v3.0.3" + }, + "time": "2020-12-07T05:54:22+00:00" + }, + { + "name": "topthink/think-multi-app", + "version": "v1.0.14", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-multi-app.git", + "reference": "ccaad7c2d33f42cb1cc2a78d6610aaec02cea4c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-multi-app/zipball/ccaad7c2d33f42cb1cc2a78d6610aaec02cea4c3", + "reference": "ccaad7c2d33f42cb1cc2a78d6610aaec02cea4c3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1.0", + "topthink/framework": "^6.0.0" + }, + "type": "library", + "extra": { + "think": { + "services": [ + "think\\app\\Service" + ] + } + }, + "autoload": { + "psr-4": { + "think\\app\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "description": "thinkphp6 multi app support", + "support": { + "issues": "https://github.com/top-think/think-multi-app/issues", + "source": "https://github.com/top-think/think-multi-app/tree/master" + }, + "time": "2020-07-12T13:50:37+00:00" + }, + { + "name": "topthink/think-orm", + "version": "v2.0.39", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-orm.git", + "reference": "39a9d0a0e52d9b8bad9d98484d8484cdf5b683a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-orm/zipball/39a9d0a0e52d9b8bad9d98484d8484cdf5b683a7", + "reference": "39a9d0a0e52d9b8bad9d98484d8484cdf5b683a7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "ext-pdo": "*", + "php": ">=7.1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0", + "topthink/think-helper": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7|^8|^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "think\\": "src" + }, + "files": [ + "stubs/load_stubs.php" + ] + }, + "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.39" + }, + "time": "2021-02-26T10:20:00+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "9d172471e56433b5c7061006b9a766f262a3edfd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/9d172471e56433b5c7061006b9a766f262a3edfd", + "reference": "9d172471e56433b5c7061006b9a766f262a3edfd", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/annotations": "*", + "ext-json": "*", + "php": ">=7.2", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpunit/phpunit": ">=8" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "autoload": { + "psr-4": { + "OpenApi\\": "src" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Robert Allen", + "email": "zircote@gmail.com" + }, + { + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "https://bfanger.nl" + }, + { + "name": "Martin Rademacher", + "email": "mano@radebatz.net", + "homepage": "https://radebatz.net" + } + ], + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", + "keywords": [ + "api", + "json", + "rest", + "service discovery" + ], + "support": { + "issues": "https://github.com/zircote/swagger-php/issues", + "source": "https://github.com/zircote/swagger-php/tree/3.1.0" + }, + "time": "2020-09-03T20:18:43+00:00" + } + ], + "packages-dev": [ + { + "name": "symfony/polyfill-php80", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-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" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + }, + "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": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v4.4.20", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a1eab2f69906dc83c5ddba4632180260d0ab4f7f", + "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5", + "symfony/polyfill-php80": "^1.15" + }, + "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.20" + }, + "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": "2021-01-27T09:09:26+00:00" + }, + { + "name": "topthink/think-trace", + "version": "v1.4", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-trace.git", + "reference": "9a9fa8f767b6c66c5a133ad21ca1bc96ad329444" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-trace/zipball/9a9fa8f767b6c66c5a133ad21ca1bc96ad329444", + "reference": "9a9fa8f767b6c66c5a133ad21ca1bc96ad329444", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1.0", + "topthink/framework": "^6.0.0" + }, + "type": "library", + "extra": { + "think": { + "services": [ + "think\\trace\\Service" + ], + "config": { + "trace": "src/config.php" + } + } + }, + "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.4" + }, + "time": "2020-06-29T05:27:28+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1.0" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..d86764c --- /dev/null +++ b/config/app.php @@ -0,0 +1,36 @@ + env('app.host', ''), + //调试开关 + 'app_debug' => env('app_debug', false), + // 应用的命名空间 + 'app_namespace' => '', + // 是否启用路由 + 'with_route' => true, + // 是否启用事件 + 'with_event' => 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' => true, +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..d131ba8 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,20 @@ + 1, // 权限开关 + 'auth_type' => 1, // 认证方式,1为实时认证;2为登录认证。 + 'auth_group' => 'system_auth_group', // 用户组数据不带前缀表名 + 'auth_group_access' => 'system_auth_group_access', // 用户-用户组关系不带前缀表名 + 'auth_rule' => 'system_auth_rule', // 权限规则不带前缀表名 + 'auth_user' => 'system_user', // 用户信息不带前缀表名 + 'auth_filter_model' => [ + 'api/login/login' => '用户登录', + 'api/manager/permissMenu' => '用户获取菜单', + 'api/manager/read' => '用户个人信息', + 'api/manager/passwordReset' => '修改密码', + 'api/Dashboard/index' => '仪表盘', + ] +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..a8d69d2 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,29 @@ + env('cache.driver', 'file'), + + // 缓存连接方式配置 + 'stores' => [ + 'file' => [ + // 驱动方式 + 'type' => 'File', + // 缓存保存目录 + 'path' => '', + // 缓存前缀 + 'prefix' => '', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, + // 缓存标签前缀 + 'tag_prefix' => 'tag:', + // 序列化机制 例如 ['serialize', 'unserialize'] + 'serialize' => [], + ], + // 更多的缓存连接 + ], +]; diff --git a/config/captcha.php b/config/captcha.php new file mode 100644 index 0000000..4be502c --- /dev/null +++ b/config/captcha.php @@ -0,0 +1,39 @@ + 4, + // 验证码字符集合 + 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', + // 验证码过期时间 + 'expire' => 1800, + // 是否使用中文验证码 + 'useZh' => false, + // 是否使用算术验证码 + 'math' => true, + // 是否使用背景图 + 'useImgBg' => false, + //验证码字符大小 + 'fontSize' => 18, + // 是否使用混淆曲线 + 'useCurve' => true, + //是否添加杂点 + 'useNoise' => true, + // 验证码字体 不设置则随机 + 'fontttf' => '', + //背景颜色 + 'bg' => [243, 251, 254], + // 验证码图片高度 + 'imageH' => 48, + // 验证码图片宽度 + 'imageW' => 130, + + // 添加额外的验证码设置 + // verify => [ + // 'length'=>4, + // ... + //], +]; diff --git a/config/console.php b/config/console.php new file mode 100644 index 0000000..a818a98 --- /dev/null +++ b/config/console.php @@ -0,0 +1,9 @@ + [ + ], +]; diff --git a/config/cookie.php b/config/cookie.php new file mode 100644 index 0000000..f728024 --- /dev/null +++ b/config/cookie.php @@ -0,0 +1,18 @@ + 0, + // cookie 保存路径 + 'path' => '/', + // cookie 有效域名 + 'domain' => '', + // cookie 启用安全传输 + 'secure' => false, + // httponly设置 + 'httponly' => false, + // 是否使用 setcookie + 'setcookie' => true, +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..5e9da7d --- /dev/null +++ b/config/database.php @@ -0,0 +1,62 @@ + env('database.driver', 'mysql'), + + // 自定义时间查询规则 + 'time_query_rule' => [], + + // 自动写入时间戳字段 + // true为自动识别类型 false关闭 + // 字符串则明确指定时间字段类型 支持 int timestamp datetime date + 'auto_timestamp' => true, + + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + + // 数据库连接配置信息 + 'connections' => [ + 'mysql' => [ + // 数据库类型 + 'type' => env('database.type', 'mysql'), + // 服务器地址 + 'hostname' => env('database.hostname', '122.114.59.173'), + // 数据库名 + 'database' => env('database.database', 'bmz_base'), + // 用户名 + 'username' => env('database.username', 'bmz_base'), + // 密码 + 'password' => env('database.password', 'C2dA3FPLZxhDjKnr'), + // 端口 + 'hostport' => env('database.hostport', '3306'), + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => env('database.charset', 'utf8mb4'), + // 数据库表前缀 + 'prefix' => env('database.prefix', 'bmz_'), + + // 数据库部署方式: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, + // 字段缓存路径 + 'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR, + ], + + // 更多的数据库配置信息 + ], +]; diff --git a/config/dict.php b/config/dict.php new file mode 100644 index 0000000..69bcbb5 --- /dev/null +++ b/config/dict.php @@ -0,0 +1,53 @@ + + + array ( + 'status' => + array ( + 0 => '禁用', + 1 => '启用', + ), + 'sex' => + array ( + 1 => '女', + 2 => '男', + ), + 'column_type' => + array ( + 0 => '列表', + 1 => '单页', + 2 => '外部链接', + 3 => '留言板', + ), + 'ad_type' => + array ( + 0 => '固定广告', + 1 => '幻灯广告', + 2 => '对联广告', + 3 => '漂浮广告', + ), + 'image_storage_type' => + array ( + 0 => '全部', + 1 => '商务', + 2 => '建筑', + 3 => '山水', + 4 => '风景', + 5 => '办公', + 6 => '天空', + 7 => '家具', + 8 => '汽车', + 9 => '服装', + 10 => '工厂', + 11 => '安全', + 12 => '峡谷', + ), +) +, +]; + \ No newline at end of file diff --git a/config/dictionary.php b/config/dictionary.php new file mode 100644 index 0000000..69a63c7 --- /dev/null +++ b/config/dictionary.php @@ -0,0 +1,86 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 数据字典配置 +// +---------------------------------------------------------------------- + +return [ + //登录数据字典 + '0' => 'ok', + '100' => '未知错误', + '101' => '数据不存在', + '102' => '站点不存在,请联系官方客服授权!!!', + '200' => 'ok', + '201' => '添加成功', + '202' => '添加失败', + '203' => '修改成功', + '204' => '修改失败或者没做任何修改', + '205' => '删除成功', + '206' => '删除失败', + '207' => '状态更改成功', + '208' => '状态更改失败', + '209' => '保存成功', + '210' => '保存失败', + '403' => '很抱歉您没有操作权限,请联系站点管理员!!!', + '500' => 'error', + '40500' => '恭喜,登陆成功!!!', + '40501' => '用户名不正确!!!', + '40502' => '用户名不存在!!!', + '40503' => '该用户名已被禁用!!!', + '40504' => '密码不正确!!!', + '40505' => '未检测到正确的用户名!!!', + '40506' => '验证码不正确!!!', + '40507' => '密码错误次数过多,请半小时后重试或联系管理员!!!', + '40508' => '您的ip已被禁止访问,请联系管理员!!!', + '40509' => 'refresh_token已过期', + '40510' => 'token已过期', + '40511' => '您没有操作权限请联系站点管理员!!!', + '40512' => 'token无效或验证失败!!!', + //短信Api数据字典 + '40601' => '请输入正确的手机号!!!', + '40602' => '短信服务商数据不存在或被禁用!!!', + '40603' => '短信模板不存在或被禁用!!!', + '40604' => '请输入4位验证码!!!', + '40605' => '验证码不正确,请重新输入!!!', + + //数据库字典 + 'sqlfields' => [ + 'status' => [ + 0 => '禁用', + 1 => '启用', + ], + 'sex' => [ + 1 => '女', + 2 => '男' + ], + /*栏目类型*/ + 'column_type' => [ + 0 => '列表', + 1 => '单页', + 2 => '外部链接', + 3 => '留言板', + ], + /*广告类型*/ + 'ad_type' => [ + 0 => '固定广告', + 1 => '幻灯广告', + 2 => '对联广告', + 3 => '漂浮广告', + ], + + ], + 'upload'=>[ + 'cdn_img_host'=>'http://xxxx.com/' + ], + + +]; diff --git a/config/filesystem.php b/config/filesystem.php new file mode 100644 index 0000000..965297e --- /dev/null +++ b/config/filesystem.php @@ -0,0 +1,24 @@ + 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', + ], + // 更多的磁盘配置信息 + ], +]; diff --git a/config/lang.php b/config/lang.php new file mode 100644 index 0000000..e3b4986 --- /dev/null +++ b/config/lang.php @@ -0,0 +1,25 @@ + env('lang.default_lang', 'zh-cn'), + // 允许的语言列表 + 'allow_lang_list' => [], + // 多语言自动侦测变量名 + 'detect_var' => 'lang', + // 是否使用Cookie记录 + 'use_cookie' => true, + // 多语言cookie变量 + 'cookie_var' => 'think_lang', + // 扩展语言包 + 'extend_list' => [], + // Accept-Language转义为对应语言包名称 + 'accept_language' => [ + 'zh-hans-cn' => 'zh-cn', + ], + // 是否支持语言分组 + 'allow_group' => false, +]; diff --git a/config/log.php b/config/log.php new file mode 100644 index 0000000..ea24ff9 --- /dev/null +++ b/config/log.php @@ -0,0 +1,45 @@ + 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, + ], + // 其它日志通道配置 + ], + +]; diff --git a/config/middleware.php b/config/middleware.php new file mode 100644 index 0000000..7e1972f --- /dev/null +++ b/config/middleware.php @@ -0,0 +1,8 @@ + [], + // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行 + 'priority' => [], +]; diff --git a/config/route.php b/config/route.php new file mode 100644 index 0000000..955eeec --- /dev/null +++ b/config/route.php @@ -0,0 +1,45 @@ + '/', + // 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' => 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', +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..c1ef6e1 --- /dev/null +++ b/config/session.php @@ -0,0 +1,19 @@ + 'PHPSESSID', + // SESSION_ID的提交变量,解决flash上传跨域 + 'var_session_id' => '', + // 驱动方式 支持file cache + 'type' => 'file', + // 存储连接标识 当type使用cache的时候有效 + 'store' => null, + // 过期时间 + 'expire' => 1440, + // 前缀 + 'prefix' => '', +]; diff --git a/config/trace.php b/config/trace.php new file mode 100644 index 0000000..fad2392 --- /dev/null +++ b/config/trace.php @@ -0,0 +1,10 @@ + 'Html', + // 读取的日志通道名 + 'channel' => '', +]; diff --git a/config/view.php b/config/view.php new file mode 100644 index 0000000..01259a0 --- /dev/null +++ b/config/view.php @@ -0,0 +1,25 @@ + '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' => '}', +]; diff --git a/database/migrations/20181113071924_create_rules_table.php b/database/migrations/20181113071924_create_rules_table.php new file mode 100644 index 0000000..7b93746 --- /dev/null +++ b/database/migrations/20181113071924_create_rules_table.php @@ -0,0 +1,108 @@ +getDbConfig(); + + $adapter = AdapterFactory::instance()->getAdapter($options['adapter'], $options); + + if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) { + $adapter = AdapterFactory::instance()->getWrapper('prefix', $adapter); + } + + $this->setAdapter( $adapter); + } + + /** + * 获取数据库配置 + * @return array + */ + protected function getDbConfig(): array + { + $default = config('tauthz.database.connection') ?: config('database.default'); + + $config = config("database.connections.{$default}"); + + if (0 == $config['deploy']) { + $dbConfig = [ + 'adapter' => $config['type'], + 'host' => $config['hostname'], + 'name' => $config['database'], + 'user' => $config['username'], + 'pass' => $config['password'], + 'port' => $config['hostport'], + 'charset' => $config['charset'], + 'table_prefix' => $config['prefix'], + ]; + } else { + $dbConfig = [ + 'adapter' => explode(',', $config['type'])[0], + 'host' => explode(',', $config['hostname'])[0], + 'name' => explode(',', $config['database'])[0], + 'user' => explode(',', $config['username'])[0], + 'pass' => explode(',', $config['password'])[0], + 'port' => explode(',', $config['hostport'])[0], + 'charset' => explode(',', $config['charset'])[0], + 'table_prefix' => explode(',', $config['prefix'])[0], + ]; + } + + $table = config('database.migration_table', 'migrations'); + + $dbConfig['default_migration_table'] = $dbConfig['table_prefix'] . $table; + + return $dbConfig; + } + + /** + * Change Method. + * + * Write your reversible migrations using this method. + * + * More information on writing migrations is available here: + * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class + * + * The following commands can be used in this method and Phinx will + * automatically reverse them when rolling back: + * + * createTable + * renameTable + * addColumn + * renameColumn + * addIndex + * addForeignKey + * + * Remember to call "create()" or "update()" and NOT "save()" when working + * with the Table class. + */ + public function up() + { + $default = config('tauthz.default'); + $table = $this->table(config('tauthz.enforcers.'.$default.'.database.rules_name')); + $table->addColumn('ptype', 'string', ['null' => true]) + ->addColumn('v0', 'string', ['null' => true]) + ->addColumn('v1', 'string', ['null' => true]) + ->addColumn('v2', 'string', ['null' => true]) + ->addColumn('v3', 'string', ['null' => true]) + ->addColumn('v4', 'string', ['null' => true]) + ->addColumn('v5', 'string', ['null' => true]) + ->create(); + } + + public function down() + { + $default = config('tauthz.default'); + $table = $this->table(config('tauthz.enforcers.'.$default.'.database.rules_name')); + $table->drop(); + } +} diff --git a/extend/.gitignore b/extend/.gitignore new file mode 100644 index 0000000..b722e9e --- /dev/null +++ b/extend/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/extend/auth/JwtAuth.php b/extend/auth/JwtAuth.php new file mode 100644 index 0000000..d46c85f --- /dev/null +++ b/extend/auth/JwtAuth.php @@ -0,0 +1,189 @@ +host = $_config['host']; + $this->jti_nonce = $_config['jti_nonce']; + $this->expire_time = $_config['expire_time']; + $this->token_key = $_config['token_key']; + //定义加密配置 + $this->config = Configuration::forSymmetricSigner( + new Sha256(), + InMemory::plainText($this->token_key) + ); + } + + /** + * 禁止克隆 + */ + private function __clone() + { + // TODO: Implement __clone() method. + } + + /** + * 返回单例对象 + * @return JwtAuth|null + */ + public static function getInstance() + { + if (!self::$instance instanceof self) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * 设置jwt密钥并返回当前实例对象 + * @return $this + */ + public function setToken($userdata = []) + { + //读取用户信息 + $this->userdata = $userdata; + $this->createToken(); + return $this; + } + + /** + * 获取当前生成的jwt密钥 + * @return string + */ + public function getToken() + { + return $this->token; + } + + public function checkToken($token) + { + return $this->checkJwtToken($token); + } + + /** + * 创建刷新token + */ + public function createRefreshToken(Token $token) + { + $refresh_token = password_hash(mt_rand(99999, 999999) . microtime(), PASSWORD_DEFAULT); + $data = [ + 'refresh_token' => $refresh_token, + 'token' => $this->token, + 'user_id' => $this->userdata['id'], + 'site_id' => $this->userdata['site_id'] + ]; + $token->add($data); + return $refresh_token; + } + + /** + * 创建jwttoken + */ + private function createToken() + { + $now = new \DateTimeImmutable(); + $token = $this->config->builder() + // jwt签发者 + ->issuedBy('ahbmz.com') + // 配置访问对象Configures the audience (aud claim) + ->permittedFor($this->host) + // jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 + ->identifiedBy($this->jti_nonce) + // jwt的签发时间 + ->issuedAt($now) + // Configures the time that the token can be used (nbf claim) + ->canOnlyBeUsedAfter($now) + // Configures the expiration time of the token (exp claim) + ->expiresAt($now->modify($this->expire_time)) + // Configures a new claim, called "uid" + ->withClaim('user_id', $this->userdata['id']) + ->withClaim('site_id', $this->userdata['site_id']) + // Configures a new header, called "foo" +// ->withHeader('foo', 'bar') + // Builds a new token + ->getToken($this->config->signer(), $this->config->signingKey()); + +// halt($token->claims()->get('uid')); + $this->token = $token->toString(); + } + + private function checkJwtToken($header_token) + { + if (empty($header_token)) { + return false; + } + $token = $this->config->parser()->parse($header_token); + $constraint = new Constraint\SignedWith($this->config->signer(), $this->config->verificationKey()); + $this->config->setValidationConstraints($constraint); + $constraints = $this->config->validationConstraints(); + + //验证 + if ($this->config->validator()->validate($token, ...$constraints)) { + $now = new \DateTimeImmutable(); +// $clock = new FrozenClock($now); +// $a = new Constraint\LooseValidAt($clock,new \DateInterval('P2W')); + //判断是否过期 + if ($token->isExpired($now)) { + return false; + } + + //判断当前jwt是否生效 + if (!$token->isMinimumTimeBefore($now)) { + return false; + } + + //判断域名是否正确 + if (!$token->isPermittedFor($this->host)) { + return false; + } + + //判断签发者是否正确 + if (!$token->hasBeenIssuedBy('ahbmz.com')) { + return false; + } + + return [ + 'user_id' => $token->claims()->get('user_id'), + 'site_id' => $token->claims()->get('site_id'), + ]; + } + return false; + } + +} \ No newline at end of file diff --git a/extend/auth/PermissAuth.php b/extend/auth/PermissAuth.php new file mode 100644 index 0000000..01bcf7c --- /dev/null +++ b/extend/auth/PermissAuth.php @@ -0,0 +1,438 @@ + 1, // 权限开关 + 'auth_type' => 1, // 认证方式,1为实时认证;2为登录认证。 + 'auth_group' => 'auth_group', // 用户组数据表名 + 'auth_group_access' => 'auth_group_access', // 用户-用户组关系表 + 'auth_rule' => 'auth_rule', // 权限规则表 + 'auth_user' => 'user', // 用户信息表 + ]; + + /** + * 类架构函数 + * Auth constructor. + */ + private function __construct() + { + //可设置配置项 auth, 此配置项为数组。 + if ($auth = Config::get('auth')) { + $this->config = array_merge($this->config, $auth); + } + // 初始化request + $this->request = Request::instance(); + + if (!defined('SITE_ID')) { + return false; + } + $this->site_id = SITE_ID; + + + } + + public function __clone() + { + // TODO: Implement __clone() method. + } + + /** + * 初始化 + * @access public + * @param array $options 参数 + * @return \think\Request + */ + public static function getInstance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new self($options); + } + return self::$instance; + } + + /** + * 检查权限 + * @param $name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组 + * @param $uid int 认证用户的id + * @param int $type 认证类型 + * @param string $mode 执行check的模式 + * @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证 + * @return bool 通过验证返回true;失败返回false + */ + public function check($name, $uid, $type = 1, $mode = 'url', $relation = 'or') + { + if (!$this->config['auth_on']) { + return true; + } + + //判断是否配置文件中放行的url + if (in_array(strtolower($name), array_keys(array_change_key_case($this->config['auth_filter_model'])))) { + return true; + } + // 获取用户需要验证的所有有效规则列表 + $authList = $this->getAuthList($uid, $type, false, 2); + if (is_string($name)) { + $name = strtolower($name); + if (strpos($name, ',') !== false) { + $name = explode(',', $name); + } else { + $name = [$name]; + } + } + $list = []; //保存验证通过的规则名 + if ('url' == $mode) { + $REQUEST = unserialize(strtolower(serialize($this->request->param()))); + } + + foreach ($authList as $auth) { + $query = preg_replace('/^.+\?/U', '', $auth); + if ('url' == $mode && $query != $auth) { + parse_str($query, $param); //解析规则中的param + $intersect = array_intersect_assoc($REQUEST, $param); + $auth = preg_replace('/\?.*$/U', '', $auth); + if (in_array($auth, $name) && $intersect == $param) { + //如果节点相符且url参数满足 + $list[] = $auth; + } + } else { + if (in_array($auth, $name)) { + $list[] = $auth; + } + } + } + if ('or' == $relation && !empty($list)) { + return true; + } + $diff = array_diff($name, $list); + if ('and' == $relation && empty($diff)) { + return true; + } + + return false; + } + + /** + * 根据用户id获取用户组,返回值为数组 + * @param $uid int 用户id + * @return array 用户所属的用户组 array( + * array('uid'=>'用户id','group_id'=>'用户组id','title'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'), + * ...) + */ + public function getUserGroups(int $uid = 0) + { + static $groups = []; + if (isset($groups[$uid])) { + return $groups[$uid]; + } + // 转换表名 + $auth_group_access = $this->config['auth_group_access']; + $auth_group = $this->config['auth_group']; + // 执行查询 + $user_groups = Db::view([$auth_group_access => $auth_group_access], 'uid,group_id') + ->view([$auth_group => $auth_group], 'id,title,role_code,rules', "{$auth_group_access}.group_id={$auth_group}.id", 'LEFT') + ->where("{$auth_group_access}.uid='{$uid}' and {$auth_group}.status='1'") + ->select()->toArray(); + $groups[$uid] = $user_groups ?: []; + + return $groups[$uid]; + } + + /** + * 获取系统所有角色 + * @return \think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function getGroups() + { + $title = $this->request->param('title', ''); + $role_code = $this->request->param('role_code', ''); + $page = $this->request->param('page', 1, 'intval'); + $limit = $this->request->param('limit', 10, 'intval'); + $_db = Db::table($this->config['auth_group']); + $_db->where('site_id', $this->site_id); + //搜索角色名称 + empty($title) ? '' : $_db->where('title', 'like', '%' . $title . '%'); + empty($role_code) ? '' : $_db->where('role_code', 'like', '%' . $role_code . '%'); + + $_groups = $_db->where('type', 1)->page($page, $limit)->select(); + if (!$_groups->isEmpty()) { + $_groups = $_groups->toArray(); + array_walk($_groups, function (&$v, $k) { + $v['update_time'] = date('Y-m-d H:i:s', $v['update_time']); + $v['create_time'] = date('Y-m-d H:i:s', $v['create_time']); + }); + } + return $_groups; + } + + /** + * 根据用户角色组ID获取用户角色组权限菜单 + * @param string $id + * @return array|void + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + public function getGroupRule(int $id = 0) + { + if (!$id) { + return; + } + $_db = Db::table($this->config['auth_group']); + $_db->where('site_id', $this->site_id); + $_data = $_db->field('rules')->find($id); + if (empty($_data)) { + return; + } + if(SITE_ID == 1){ + $_rules = Db::table($this->config['auth_rule'])->select(); + }else{ + $_rules = Db::table($this->config['auth_rule'])->where('is_manager_use',0)->select(); + } + + + $_data = explode(',', $_data['rules']); + if (!$_rules->isEmpty()) { + $_rules = $_rules->toArray(); + array_walk($_rules, function (&$v) use ($_data) { + $v['title'] = $v['name']; + $v['checked'] = in_array($v['id'], $_data) ? true : false; + $v['children'] = null; + $v['open'] = true; + }); + return $_rules; + } + return; + } + + /** + * 添加用户到用户角色组关系 + * @param array $_data + * @return int|string + */ + public function addUserGroupAccess(int $user_id, array $_data = []) + { + + if (empty($_data) || empty($user_id)) { + return true; + } + $_db = Db::table($this->config['auth_group_access']); + //添加之前先删除已有的用户角色所有权限 + $_db->where('site_id', $this->site_id)->where('uid', $user_id)->delete(); + + foreach ($_data as $k => $v) { + $insert_data[$k]['site_id'] = $this->site_id; + $insert_data[$k]['uid'] = $user_id; + $insert_data[$k]['group_id'] = $v; + $insert_data[$k]['update_time'] = $insert_data[$k]['create_time'] = time(); + } + $affected = $_db->strict(false)->insertAll($insert_data); + return $affected; + } + + /** + * 添加权限菜单用户角色组 + * @param array $_data + * @return int|string + */ + public function addGroupRule(int $role_id = 0, string $menu_id = '') + { + $_data['site_id'] = $this->site_id; + $_data['update_time'] = time(); + $_data['rules'] = $menu_id; + $_db = Db::table($this->config['auth_group']); + $_db->where('id', $role_id); + $affected = $_db->strict(false)->save($_data); + ActionLog::getInstance()->write($_data, [], $_db->getLastSql()); + return $affected; + } + + /** + * 添加系统用户角色组 + * @param array $_data + * @return int|string + */ + public function addGroup(array $_data = []) + { + $_data['site_id'] = $this->site_id; + $_data['update_time'] = $_data['create_time'] = time(); + $_db = Db::table($this->config['auth_group']); + $insert_id = $_db->strict(false)->insertGetId($_data); + ActionLog::getInstance()->write($_data, [], $_db->getLastSql()); + return $insert_id; + } + + /** + * 修改系统用户角色组 + * @param array $_data + * @return int|string + */ + public function editGroup(array $_data = []) + { + $_data['site_id'] = $this->site_id; + $_data['update_time'] = time(); + $_db = Db::table($this->config['auth_group']); + $affected = $_db->strict(false)->save($_data); + ActionLog::getInstance()->write($_data, [], $_db->getLastSql()); + return $affected; + } + + /** + * 更改用户角色组状态 + * @param array $ids + * @return int + * @throws \think\db\exception\DbException + */ + public function statusGroup(string $ids = '', int $status = 1) + { + $_op = Db::table($this->config['auth_group']); + $ids = explode(',', $ids); + $_op->where('site_id', SITE_ID); + $_op->where('id', 'in', $ids); + $affected = $_op->update(['status' => $status]); + ActionLog::getInstance()->write($ids, [], $_op->getLastSql()); + return $affected; + } + + /** + * 删除系统用户角色组 + * @param array $ids + * @return int + * @throws \think\db\exception\DbException + */ + public function deleteGroup($ids = []) + { + $_op = Db::table($this->config['auth_group']); + $ids = explode(',', $ids); + $_op->where('site_id', SITE_ID); + $affected = $_op->where('id', 'in', $ids)->delete(); + ActionLog::getInstance()->write($ids, [], $_op->getLastSql()); + return $affected; + } + + /** + * 获得权限列表 + * @param integer $uid 用户id + * @param integer $type + * @param integer $allfield 是否获取rule多字段,默认为false + * @param integer $menu_type 0:获取菜单(默认) 1:获取按钮 2:获取所有 + * @return array + */ + public function getAuthList($uid = 0, $type = [], $allfield = false, $menu_type = 0) + { + $uid = empty($uid) ? UID : $uid; + + static $_authList = []; //保存用户验证通过的权限列表 + $t = implode(',', (array)$type); + if (isset($_authList[$uid . $t])) { + return $_authList[$uid . $t]; + } + if (2 == $this->config['auth_type'] && Session::has('_auth_list_' . $uid . $t)) { + return Session::get('_auth_list_' . $uid . $t); + } + //读取用户所属用户组 + $groups = $this->getUserGroups($uid); + $ids = []; //保存用户所属用户组设置的所有权限规则id + foreach ($groups as $g) { + $ids = array_merge($ids, explode(',', trim($g['rules'], ','))); + } + $ids = array_unique($ids); + if (empty($ids)) { + $_authList[$uid . $t] = []; + return []; + } + $map = [ + 'type' => $type, + 'status' => 1, + ]; + $menu_type == 2 ? '' : $map['menu_type'] = $menu_type; //默认获取所有 + config('app.app_debug') ?: $map['is_dev'] = 0;//开发者模式 + + //读取用户组所有权限规则 + $_op = Db::table($this->config['auth_rule']); + $_op->where($map)->where('id', 'in', $ids)->where('delete_time', null); + $rules = $_op->order('sort DESC,create_time DESC')->select(); + //$rules = Db::name($this->config['auth_rule'])->where($map)->field('condition,model_name')->select()->toArray(); + //循环规则,判断结果。 + $authList = []; // + foreach ($rules as $rule) { + if (!empty($rule['condition'])) { + //根据condition进行验证 + $user = $this->getUserInfo($uid); //获取用户信息,一维数组 + $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']); + //dump($command); //debug + @(eval('$condition=(' . $command . ');')); + if ($condition) { + if ($allfield) { + $rule['show'] = empty($rule['show']) ? false : true; //前端需要 + $authList[] = $rule; + } else { + $authList[] = strtolower($rule['model_name']); + } + } + } else { + //只要存在就记录 + if ($allfield) { + $authList[] = $rule; + } else { + $authList[] = strtolower($rule['model_name']); + } + } + } + $_authList[$uid . $t] = $authList; + if (2 == $this->config['auth_type']) { + //规则列表结果保存到session + //Session::set('_auth_list_' . $uid . $t, $authList); + } + return array_unique($authList, SORT_REGULAR); + } + + /** + * 获得用户资料,根据自己的情况读取数据库 + */ + protected function getUserInfo($uid) + { + static $userinfo = []; + + $user = Db::table($this->config['auth_user']); + // 获取用户表主键 + $_pk = is_string($user->getPk()) ? $user->getPk() : 'uid'; + if (!isset($userinfo[$uid])) { + $userinfo[$uid] = $user->where($_pk, $uid)->find(); + } + + return $userinfo[$uid]; + } + +} \ No newline at end of file diff --git a/extend/auth/SiteAuth.php b/extend/auth/SiteAuth.php new file mode 100644 index 0000000..42f3f36 --- /dev/null +++ b/extend/auth/SiteAuth.php @@ -0,0 +1,67 @@ + 'require|activeUrl'])->check(['host' => $host]); + + //返回数据 + $_data = Db::name($this->table)->where('host', $host)->find(); + return $_data; + } + + /** + * 更新站点信息 + */ + public function updateSite() + { + + } + +} \ No newline at end of file diff --git a/extend/service/ActionLog.php b/extend/service/ActionLog.php new file mode 100644 index 0000000..a137aec --- /dev/null +++ b/extend/service/ActionLog.php @@ -0,0 +1,219 @@ +controller(), $this->_controller)) { + return false; + } + + $_param = request()->param(); + $this->_data = array_merge($_data, $_param); + $this->_origin = $_origin; + $this->sys_manager = $this->getSysManager(); + //增加日志记录 + $data = [ + 'site_id' => defined('SITE_ID') ? SITE_ID : $this->_data['site_id'] ?? 0, + 'user_id' => defined('UID') ? UID : $this->_data['user_id'] ?? 0, + 'username' => $this->sys_manager['username'] ?? '', + 'action_ip' => ip2long(request()->ip()), + 'request_method' => request()->method(), + 'request_param' => json_encode($_param), + 'model' => request()->baseUrl(), + 'url' => request()->host() . request()->url(), + 'spend_time' => round(microtime(true) - request()->time(), 4), + 'record_id' => $_data['id'] ?? $_param['id'] ?? 0, //数据表id + 'remark' => $this->replaceLog(), //admin在2018年12月23 17:20:48复制了栏目。权限节点cloud/column/copy,记录编号1。 + 'data' => json_encode($_data), + 'origin' => json_encode($_origin), + 'sql' => $_sql, + 'client_browser' => get_client_browser(), + 'client_os' => get_client_os(), + 'client_device' => request()->header('client-device') ?? get_client_os(), + 'update_time' => time(), + 'create_time' => time(), + ]; + return Db::table($this->table)->insert($data); + } + + /** + * 写入日志数据 + * @return string|string[]|void + * @throws \ReflectionException + */ + private function replaceLog() + { + //通过注解获取以下信息数据再解析 + // @Log [sys_name]在[sys_time]创建了管理员:[username] + // @Log [sys_name]在[sys_time]修改了[origin_username]管理员账号为[username] + + //解析数据 + $annot = $this->getAnnotations(); + if (!$annot) { + return '未检测到日志信息模型,请检查CURR_THIS反射常量!!!'; + } + preg_match_all('/\[(\S+?)\]/', $annot, $match); + list($find, $replace) = $match; + foreach ($replace as $k => $v) { + switch ($v) { + case 'sys_name': + $replace[$k] = $this->sys_manager['username'] ?? ''; + break; + case 'sys_time': + $replace[$k] = date('Y-m-d H:i:s'); + break; + default: + if (strpos($v, 'origin_') === 0) { + //判断是否含有源数据的字段标识 origin_ + $replace[$k] = $this->_origin[str_replace('origin_', '', $v)]; + } elseif (strpos($v, 'dictionary_') === 0) { + //判断是否含有数据字典的标识 dictionary_ + $filter_name = str_replace('dictionary_', '', $v); + //如果字典有值就替换 否则保持原数据 + $replace[$k] = $this->getDictionary($filter_name)[$this->_data[$filter_name]] ?? $this->_data[$filter_name]; + } else { + //默认返回原数据值 + $replace[$k] = $this->_data[$v] ?? '{变量未知}'; + } + } + } + $annot = str_replace($find, $replace, $annot); + return $annot; + } + + /** + * 通过反射类获取PHP类的注释 + * @return string|void + * @throws \ReflectionException + */ + private function getAnnotations() + { + if (!defined('CURR_THIS')) { + return; + } + $class = new \ReflectionClass(CURR_THIS); + $docComment = $class->getMethod(request()->action())->getDocComment(); + return get_doc_comment($docComment, 'Log'); +// return '[sys_name]在[sys_time]创建了管理员账号:[username]'; +// return '[sys_name]在[sys_time]修改了[origin_username]管理员账号为:[username]'; //指定字段后的修改变化 +// return '[sys_name]在[sys_time]删除了管理员账号:[username]'; +// return '[sys_name]在[sys_time]:更改了管理员状态为:[dictionary_status]'; + } + + /** + * 获取系统管理员用户信息 + * @param string $field + * @return array|\think\Model|null + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\DbException + * @throws \think\db\exception\ModelNotFoundException + */ + private function getSysManager($field = '') + { + $_db = Db::table($this->table_manager); + if ($field) { + return $_db->value($field); + } + $user_id = defined('UID') ? UID : $this->_data['user_id'] ?? 0; + if (empty($user_id)) { + return; + } + return $_db->find($user_id); + } + + /** + * 获取数据库字典数据 + * @param string $field + */ + private function getDictionary($field = '') + { + if ($field) { + return config('dictionary.sqlfields.' . $field); + } + return config('dictionary.sqlfields'); + } + + +} \ No newline at end of file diff --git a/extend/test/Test.php b/extend/test/Test.php new file mode 100644 index 0000000..31cd272 --- /dev/null +++ b/extend/test/Test.php @@ -0,0 +1,26 @@ +param()); + } +} \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..d9ee23c --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,8 @@ + + Options +FollowSymlinks -Multiviews + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L] + diff --git a/public/ajax.html b/public/ajax.html new file mode 100644 index 0000000..5d56174 --- /dev/null +++ b/public/ajax.html @@ -0,0 +1,38 @@ + + + + + Title + + + + + + + + + + \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e71815a Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..e3c0fe9 --- /dev/null +++ b/public/index.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- + +// [ 应用入口文件 ] +namespace think; + +require __DIR__ . '/../vendor/autoload.php'; + +// 执行HTTP应用并响应 +$http = (new App())->http; + +$response = $http->run(); + +$response->send(); + +$http->end($response); diff --git a/public/one.js b/public/one.js new file mode 100644 index 0000000..675402a --- /dev/null +++ b/public/one.js @@ -0,0 +1,635 @@ +/*app/system/include/static2/vendor/babel-external-helpers/babel-external-helpers.min.js*/ +(function(root,factory){if(typeof define==="function"&&define.amd){define(["exports"],factory);}else if(typeof exports==="object"){factory(exports);}else{factory(root.babelHelpers={});}})(this,function(global){var babelHelpers=global;babelHelpers.typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj;}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol?"symbol":typeof obj;};babelHelpers.jsx=function(){var REACT_ELEMENT_TYPE=typeof Symbol==="function"&&Symbol.for&&Symbol.for("react.element")||0xeac7;return function createRawReactElement(type,props,key,children){var defaultProps=type&&type.defaultProps;var childrenLength=arguments.length-3;if(!props&&childrenLength!==0){props={};}if(props&&defaultProps){for(var propName in defaultProps){if(props[propName]===void 0){props[propName]=defaultProps[propName];}}}else if(!props){props=defaultProps||{};}if(childrenLength===1){props.children=children;}else if(childrenLength>1){var childArray=Array(childrenLength);for(var i=0;i=0)continue;if(!Object.prototype.hasOwnProperty.call(obj,i))continue;target[i]=obj[i];}return target;};babelHelpers.possibleConstructorReturn=function(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;};babelHelpers.selfGlobal=typeof global==="undefined"?self:global;babelHelpers.set=function set(object,property,value,receiver){var desc=Object.getOwnPropertyDescriptor(object,property);if(desc===undefined){var parent=Object.getPrototypeOf(object);if(parent!==null){set(parent,property,value,receiver);}}else if("value"in desc&&desc.writable){desc.value=value;}else{var setter=desc.set;if(setter!==undefined){setter.call(receiver,value);}}return value;};babelHelpers.slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"])_i["return"]();}finally{if(_d)throw _e;}}return _arr;}return function(arr,i){if(Array.isArray(arr)){return arr;}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i);}else{throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}();babelHelpers.slicedToArrayLoose=function(arr,i){if(Array.isArray(arr)){return arr;}else if(Symbol.iterator in Object(arr)){var _arr=[];for(var _iterator=arr[Symbol.iterator](),_step;!(_step=_iterator.next()).done;){_arr.push(_step.value);if(i&&_arr.length===i)break;}return _arr;}else{throw new TypeError("Invalid attempt to destructure non-iterable instance");}};babelHelpers.taggedTemplateLiteral=function(strings,raw){return Object.freeze(Object.defineProperties(strings,{raw:{value:Object.freeze(raw)}}));};babelHelpers.taggedTemplateLiteralLoose=function(strings,raw){strings.raw=raw;return strings;};babelHelpers.temporalRef=function(val,name,undef){if(val===undef){throw new ReferenceError(name+" is not defined - temporal dead zone");}else{return val;}};babelHelpers.temporalUndefined={};babelHelpers.toArray=function(arr){return Array.isArray(arr)?arr:Array.from(arr);};babelHelpers.toConsumableArray=function(arr){if(Array.isArray(arr)){for(var i=0,arr2=Array(arr.length);ia?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; +}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("