diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..669bf53 --- /dev/null +++ b/.env.example @@ -0,0 +1,30 @@ +APP_NAME=skeleton +APP_ENV=dev + +DB_DRIVER=mysql +DB_HOST=localhost +DB_PORT=3306 +DB_DATABASE=mine +DB_USERNAME=root +DB_PASSWORD=root +DB_CHARSET=utf8mb4 +DB_COLLATION=utf8mb4_unicode_ci +DB_PREFIX= + +REDIS_HOST=localhost +REDIS_AUTH= +REDIS_PORT=6379 +REDIS_DB=0 + +AMQP_HOST = 127.0.0.1 +AMQP_PORT = 5672 +AMQP_USER = guest +AMQP_PASSWORD = guest +AMQP_VHOST = / +AMQP_ENABLE = false + +SUPER_ADMIN = 1000 +ADMIN_ROLE = 1000 +CONSOLE_SQL = true +JWT_SECRET = abcdefg +JWT_API_SECRET = 654321 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..caba3c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/.idea +/.vscode +/vendor +/package +/runtime +*.log +.env +.gitee +public/uploadfile/* +/resources diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ff25963 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,154 @@ +# Default Dockerfile + +ARG ALPINE_VERSION=3.15 + +FROM alpine:$ALPINE_VERSION + +LABEL maintainer="MineManage Developers " version="1.0" license="MIT" app.name="MineManage" + +ARG ALPINE_VERSION=3.15 + +# trust this project public key to trust the packages. +ADD https://php.hernandev.com/key/php-alpine.rsa.pub /etc/apk/keys/php-alpine.rsa.pub + +## +# ---------- building ---------- +## +RUN set -ex \ + # change apk source repo + && echo "https://php.hernandev.com/v$ALPINE_VERSION/php-8.1" >> /etc/apk/repositories \ + && echo "@php https://php.hernandev.com/v$ALPINE_VERSION/php-8.1" >> /etc/apk/repositories \ + && apk update \ + && apk add --no-cache \ + # Install base packages ('ca-certificates' will install 'nghttp2-libs') + ca-certificates \ + curl \ + wget \ + tar \ + xz \ + libressl \ + tzdata \ + pcre \ + php8 \ + php8-bcmath \ + php8-curl \ + php8-ctype \ + php8-dom \ + php8-gd \ + php8-iconv \ + php8-mbstring \ + php8-mysqlnd \ + php8-openssl \ + php8-pdo \ + php8-pdo_mysql \ + php8-pdo_sqlite \ + php8-phar \ + php8-posix \ + php8-redis \ + php8-sockets \ + php8-sodium \ + php8-sysvshm \ + php8-sysvmsg \ + php8-sysvsem \ + php8-zip \ + php8-zlib \ + php8-xml \ + php8-xmlreader \ + php8-pcntl \ + php8-opcache \ + && ln -sf /usr/bin/php8 /usr/bin/php \ + && apk del --purge *-dev \ + && rm -rf /var/cache/apk/* /tmp/* /usr/share/man /usr/share/php8 \ + && php -v \ + && php -m \ + && echo -e "\033[42;37m Build Completed :).\033[0m\n" + +ARG COMPOSER_VERSION=2.3.10 + +# update +RUN set -ex \ + && apk update \ + # for extension libaio linux-headers + && apk add --no-cache libstdc++ openssl git bash php8-pear php8-dev autoconf pcre2-dev c-ares-dev zlib-dev re2c gcc g++ make \ + && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS libaio-dev openssl-dev curl-dev \ + # php extension:swoole + && ln -s /usr/bin/pecl8 /usr/local/bin/pecl \ + && pecl channel-update pecl.php.net \ + && pecl install --configureoptions 'enable-sockets="no" enable-openssl="yes" enable-http2="yes" enable-mysqlnd="no" enable-swoole-json="yes" enable-swoole-curl="yes" enable-cares="no"' swoole \ + && {\ + echo "memory_limit=1G"; \ + echo "upload_max_filesize=128M"; \ + echo "post_max_size=128M"; \ + echo "memory_limit=1G"; \ + echo "date.timezone=Asia/Shanghai"; \ + } | tee /etc/php8/conf.d/00_default.ini \ + && echo "opcache.enable_cli = 'On'" >> /etc/php8/conf.d/00_opcache.ini \ + &&{ \ + echo "extension=swoole.so";\ + echo "swoole.use_shortname = 'Off'";\ + } | tee /etc/php8/conf.d/50_swoole.ini \ + # install composer + && wget -nv -O /usr/local/bin/composer https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar \ + && chmod u+x /usr/local/bin/composer \ + # php info + && php -v \ + && php -m \ + && php --ri swoole \ + && php --ri Zend\ OPcache \ + && composer --version \ + # ---------- clear works ---------- + && apk del .build-deps \ + && rm -rf /var/cache/apk/* /tmp/* /usr/share/man /usr/local/bin/php* \ + && echo -e "\033[42;37m Build Completed :).\033[0m\n" + +## +# ---------- env settings ---------- +## +# --build-arg timezone=Asia/Shanghai +ARG timezone + +ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \ + # APP_ENV=dev \ + APP_SYSTEM_ENV=docker \ + SCAN_CACHEABLE=(true) + +# update +RUN set -ex \ + # ---------- some config ---------- + && cd /etc/php8 \ + # - config timezone + && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ + && echo "${TIMEZONE}" > /etc/timezone \ + && echo -e "\033[42;37m Build Completed :).\033[0m\n" + +RUN set -ex apk update \ + && apk add --no-cache libstdc++ openssl git bash autoconf pcre2-dev zlib-dev re2c gcc g++ make \ + php8-pear php8-dev php8-tokenizer php8-fileinfo php8-simplexml php8-xmlwriter \ + && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS zlib-dev libaio-dev openssl-dev curl-dev c-ares-dev \ + && pecl channel-update pecl.php.net \ + && ln -s /usr/bin/phpize8 /usr/local/bin/phpize \ + && ln -s /usr/bin/php-config8 /usr/local/bin/php-config \ + && pecl install --configureoptions 'enable-reader="yes"' xlswriter \ + && echo "extension=xlswriter.so" >> /etc/php8/conf.d/60-xlswriter.ini \ + && php -m \ + && php -v \ + && php --ri swoole \ + && mkdir -p /app-src \ + # ---------- clear works ---------- + && apk del .build-deps \ + && rm -rf /var/cache/apk/* /tmp/* /usr/share/man /usr/local/bin/php* \ + && echo -e "\033[42;37m Build Completed :).\033[0m\n" + +# fix aliyun oss wrong charset: https://github.com/aliyun/aliyun-oss-php-sdk/issues/101 +# https://github.com/docker-library/php/issues/240#issuecomment-762438977 + +RUN apk --no-cache --allow-untrusted --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ add gnu-libiconv gnu-libiconv-dev \ + # ---------- clear works ---------- + && rm -rf /var/cache/apk/* /tmp/* /usr/share/man /usr/local/bin/php* \ + && echo -e "\033[42;37m Build Completed :).\033[0m\n" + +ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so + +WORKDIR /app-src + +EXPOSE 9501 9502 9503 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 8be400f..d8288cd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,166 @@ -# hyperf-view +# 项目介绍 +

+ +

+

+ 官网 | + 文档 | + 演示 | + Hyperf官方文档 +

+ +

+ + + + +

+PHP有很多优秀的后台管理系统,但基于Swoole的后台管理系统没找到合适我自己的。 +所以就开发了一套后台管理系统。系统可以用于网站管理后台、CMS、CRM、OA、ERP等。 + +后台系统基于 Hyperf 框架开发。企业级架构分层,轻松支撑创业公司及个人前期发展使用,使用少量的服务器资源媲美静态语言的性能。 +前端使用Vue3 + Vite3 + Pinia + Arco,一端适配PC、移动端、平板 + +如果觉着还不错的话,就请点个 ⭐star 支持一下吧,这将是对我最大的支持和鼓励! + +- 腾讯云特惠专场:[点击进入](http://txy.mineadmin.com) +- 阿里云特惠专场:[点击进入](http://aly.mineadmin.com) + +## 配置Json来使用Vue完成CRUD操作 +在传统的前后端分离架构下,前端必须要配置专业的前端开发人员来完成业务,这对于PHPer习惯前后端一把梭来说比较费事。 +在JQuery逐渐没落的大趋势下,使用Vue成为趋势,但从JQ到Vue转变最难的是思想。 + +MineAdmin强调快速开发,为了适应现代开发模式并且兼顾传统一把梭,特开发出了crud和form两个组件, +只需要像过去配置json的方式即可完成对后端的接口联调,从而快速完成CRUD,大大替开发人员节省了时间。 +让刚接触VUE的同学也能上手vue,体验vue的好处。 + +大神则可以自由发挥是决定使用crud组件,还是使用原生UI库来完成功能。 + +## 兔小巢 +给大家提供一个可以交流的地方:[http://ask.mineadmin.com](http://ask.mineadmin.com) + +## 前端仓库地址 +移步前端仓库 + +- [Github MineAdmin-Vue](https://github.com/kanyxmo/MineAdmin-Vue) +- [Gitee MineAdmin-Vue](https://gitee.com/xmo/MineAdmin-vue) + +## 非官方交流群 +> 以下QQ群为 MineAdmin 爱好者建立用于交流学习,请勿相信任何收费事项 + + + +## 内置功能 + +1. 用户管理,完成用户添加、修改、删除配置,支持不同用户登录后台看到不同的首页 +2. 部门管理,部门组织机构(公司、部门、小组),树结构展现支持数据权限 +3. 岗位管理,可以给用户配置所担任职务 +4. 角色管理,角色菜单权限分配、角色数据权限分配 +5. 菜单管理,配置系统菜单和按钮等 +6. 字典管理,对系统中经常使用并且固定的数据可以重复使用和维护 +7. 系统配置,系统的一些常用设置管理 +8. 操作日志,用户对系统的一些正常操作的查询 +9. 登录日志,用户登录系统的记录查询 +10. 在线用户,查看当前登录的用户 +11. 服务监控,查看当前服务器状态和PHP环境等信息 +12. 附件管理,管理当前系统上传的文件及图片等信息 +13. 数据表维护,对系统的数据表可以进行清理碎片和优化 +14. 模块管理,管理系统当前所有模块 +15. 定时任务,在线(添加、修改、删除)任务调度包含执行结果日志 +16. 代码生成,前后端代码的生成(php、vue、js、sql),支持下载和生成到模块 +17. 缓存监控,查看Redis信息和系统所使用key的信息 +18. API管理,对应用和接口管理、接口授权等功能。接口文档自动生成,输入、输出参数检查等 +19. 队列管理,消息队列管理功能、消息管理、消息发送。使用ws方式即时消息提醒(需安装rabbitMQ) + +## 环境需求 + +- Swoole >= 4.6.x 并关闭 `Short Name` +- PHP >= 8.0 并开启以下扩展: + - mbstring + - json + - pdo + - openssl + - redis + - pcntl +- Mysql >= 5.7 +- Redis >= 4.0 + + +## 下载项目 +- MineAdmin没有使用SQL文件导入安装,系统使用Migrates迁移文件形式安装和填充数据,请知悉。 + +- 项目下载,请确保已经安装了 `Composer` +```shell +git clone https://gitee.com/xmo/MineAdmin && cd MineAdmin +composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ +composer install +``` + +## 后端安装 + +打开终端,执行安装命令,按照提示,一步步完成`.env`文件的配置 +```shell +php bin/hyperf.php mine:install +``` + +待提示以下信息后 +```shell +Reset the ".env" file. Please restart the service before running +the installation command to continue the installation. +``` + +再次执行安装命令,执行Migrates数据迁移文件和SQL数据填充,完成安装。 +```shell +php bin/hyperf.php mine:install +``` + +[点这里 -> 查看运行后De常见问题](https://doc.mineadmin.com/faqs/) + +## 免责声明 +本软件不得用于开发违反国家有关政策的相关软件和应用,若因使用本软件造成的一切法律责任均与 `MineAdmin` 无关 + +## 体验地址 + +[体验地址](https://demo.mineadmin.com) +- 账号:superAdmin +- 密码:admin123 + +> 请勿添加脏数据 + +## 鸣谢 + +> 以下排名不分先后 + +[Hyperf 一款高性能企业级协程框架](https://hyperf.io/) + +[Arco 字节跳动出品的企业级设计系统](https://arco.design/) + +[Swoole PHP协程框架](https://www.swoole.com) + +[Vue](https://vuejs.org/) + +[Vite](https://vitejs.cn/) + +[Jetbrains 生产力工具](https://www.jetbrains.com/) + +## 通过 OSCS 安全认证 +[![OSCS Status](https://www.oscs1024.com/platform/badge/kanyxmo/MineAdmin.svg?size=large)](https://www.murphysec.com/dr/9ztZvuSN6OLFjCDGVo) + +## 演示图片 + + + + + + + + + + + + + + + + diff --git a/addon/Comarea/Api/AreaController.php b/addon/Comarea/Api/AreaController.php new file mode 100644 index 0000000..4fbedfd --- /dev/null +++ b/addon/Comarea/Api/AreaController.php @@ -0,0 +1,29 @@ +setHomeUrl('index'); + $user=new UserEntity(); + $setting->setUser($user); + return View::make($setting); + } +} \ No newline at end of file diff --git a/addon/Comarea/config.json b/addon/Comarea/config.json new file mode 100644 index 0000000..0adaafe --- /dev/null +++ b/addon/Comarea/config.json @@ -0,0 +1,9 @@ +{ + "name": "Comarea", + "label": "区域信息", + "description": "中国地区区域地址表,坐标表", + "installed": true, + "enabled": true, + "version": "1.0.0", + "order": 99 +} \ No newline at end of file diff --git a/addon/Comorder/config.json b/addon/Comorder/config.json new file mode 100644 index 0000000..cfc0015 --- /dev/null +++ b/addon/Comorder/config.json @@ -0,0 +1,9 @@ +{ + "name": "Comorder", + "label": "订单模块", + "description": "公共订单基础模块", + "installed": true, + "enabled": true, + "version": "1.0.0", + "order": 99 +} \ No newline at end of file diff --git a/addon/Member/config.json b/addon/Member/config.json new file mode 100644 index 0000000..b313340 --- /dev/null +++ b/addon/Member/config.json @@ -0,0 +1,9 @@ +{ + "name": "Member", + "label": "会员", + "description": "用户模块、平台统一用户中心", + "installed": true, + "enabled": true, + "version": "1.0.0", + "order": 99 +} \ No newline at end of file diff --git a/api/ApiController.php b/api/ApiController.php new file mode 100644 index 0000000..b79936d --- /dev/null +++ b/api/ApiController.php @@ -0,0 +1,85 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Api; + +use App\System\Service\SystemAppService; +use Hyperf\HttpServer\Annotation\Middlewares; +use Hyperf\HttpServer\Annotation\PostMapping; +use Builder\Exception\NormalStatusException; +use Builder\Helper\MineCode; +use Builder\MineApi; +use Hyperf\HttpServer\Annotation\Controller; +use Hyperf\HttpServer\Annotation\RequestMapping; +use Psr\Http\Message\ResponseInterface; +use Api\Middleware\VerifyInterfaceMiddleware; + +/** + * Class ApiController + * @package Api + */ +#[Controller(prefix: "api")] +class ApiController extends MineApi +{ + public const SIGN_VERSION = '1.0'; + + /** + * 获取accessToken + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + #[PostMapping("v1/getAccessToken")] + public function getAccessToken(): ResponseInterface + { + $service = container()->get(SystemAppService::class); + return $this->success($service->getAccessToken($this->request->all())); + } + + /** + * v1 版本 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[RequestMapping("v1/{method}")] + #[Middlewares([ VerifyInterfaceMiddleware::class ])] + public function v1(): ResponseInterface + { + $apiData = $this->__init(); + + try { + $class = make($apiData['class_name']); + return $class->{$apiData['method_name']}(); + } catch (\Throwable $e) { + throw new NormalStatusException( + t('mineadmin.interface_exception') . $e->getMessage(), + MineCode::INTERFACE_EXCEPTION + ); + } + } + + /** + * 初始化 + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function __init() + { + if (empty($this->request->input('apiData'))) { + throw new NormalStatusException(t('mineadmin.access_denied'), MineCode::NORMAL_STATUS); + } + + return $this->request->input('apiData'); + } +} \ No newline at end of file diff --git a/api/ApiDocController.php b/api/ApiDocController.php new file mode 100644 index 0000000..1631957 --- /dev/null +++ b/api/ApiDocController.php @@ -0,0 +1,92 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Api; + +use App\System\Service\SystemApiService; +use App\System\Service\SystemAppService; +use Hyperf\Di\Annotation\Inject; +use Hyperf\HttpServer\Annotation\GetMapping; +use Hyperf\HttpServer\Annotation\PostMapping; +use Builder\Helper\MineCode; +use Builder\MineApi; +use Hyperf\HttpServer\Annotation\Controller; +use Psr\Http\Message\ResponseInterface; + +/** + * Class ApiDocController + * @package Api + */ +#[Controller(prefix: "apiDoc")] +class ApiDocController extends MineApi +{ + /** + * @var SystemAppService + */ + #[Inject] + protected SystemAppService $systemAppService; + + /** + * @var SystemApiService + */ + #[Inject] + protected SystemApiService $systemApiService; + + /** + * 登录文档 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("login")] + public function login(): ResponseInterface + { + $app_id = $this->request->input('app_id', ''); + $app_secret = $this->request->input('app_secret', ''); + + if (empty($app_id) && empty($app_secret)) { + return $this->error(t('mineadmin.api_auth_fail'), MineCode::API_PARAMS_ERROR); + } + + if (($code = $this->systemAppService->loginDoc($app_id, $app_secret)) !== MineCode::API_VERIFY_PASS) { + return $this->error(t('mineadmin.api_auth_fail'), $code); + } + + return $this->success(); + } + + /** + * 通过app id获取接口数据 + * @param string $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getAppAndInterfaceList/{id}")] + public function getAppAndInterfaceList(string $id): ResponseInterface + { + return $this->success($this->systemAppService->getAppAndInterfaceList($id)); + } + + /** + * @param string $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getColumnList/{id}")] + public function getColumnList(string $id): ResponseInterface + { + return $this->success($this->systemApiService->getColumnListByApiId($id)); + } + +} \ No newline at end of file diff --git a/api/InterfaceApi/v1/DemoApi.php b/api/InterfaceApi/v1/DemoApi.php new file mode 100644 index 0000000..51b5d04 --- /dev/null +++ b/api/InterfaceApi/v1/DemoApi.php @@ -0,0 +1,75 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Api\InterfaceApi\v1; + +use Builder\MineResponse; +use App\System\Mapper\SystemDeptMapper; +use App\System\Mapper\SystemUserMapper; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * 演示,测试专用 + */ +class DemoApi +{ + /** + * @var SystemUserMapper + */ + protected SystemUserMapper $user; + + /** + * @var SystemDeptMapper + */ + protected SystemDeptMapper $dept; + + protected MineResponse $response; + + /** + * DemoApi constructor. + * @param SystemUserMapper $user + * @param SystemDeptMapper $dept + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function __construct(SystemUserMapper $user, SystemDeptMapper $dept) + { + $this->response = container()->get(MineResponse::class); + $this->user = $user; + $this->dept = $dept; + } + + /** + * 获取用户列表接口 + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function getUserList(): ResponseInterface + { + // 第二个参数,不进行数据权限检查,否则会拉起检测是否登录。 + return $this->response->success('请求成功', $this->user->getPageList([], false)); + } + + /** + * 获取部门列表接口 + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function getDeptList(): ResponseInterface + { + // 第二个参数,不进行数据权限检查,否则会拉起检测是否登录。 + return $this->response->success('请求成功', $this->dept->getTreeList([], false)); + } +} \ No newline at end of file diff --git a/api/Listener/ApiLogListener.php b/api/Listener/ApiLogListener.php new file mode 100644 index 0000000..330e777 --- /dev/null +++ b/api/Listener/ApiLogListener.php @@ -0,0 +1,74 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Api\Listener; + +use App\System\Service\SystemApiLogService; +use Hyperf\Event\Annotation\Listener; +use Hyperf\Event\Contract\ListenerInterface; +use Builder\Event\ApiAfter; +use Builder\Helper\Str; +use Builder\MineRequest; + +/** + * API访问日志保存 + */ +#[Listener] +class ApiLogListener implements ListenerInterface +{ + + /** + * 监听事件 + * @return string[] + */ + public function listen(): array + { + return [ + ApiAfter::class + ]; + } + + /** + * 事件处理 + * @param object $event + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(object $event): void + { + /* @var $event ApiAfter */ + $data = $event->getApiData(); + $request = container()->get(MineRequest::class); + $service = container()->get(SystemApiLogService::class); + + if (empty($data)) { + // 只记录异常日志,但并不抛出异常,程序正常走下去 + logger('Api Access Log')->error('API数据为空,访问日志无法记录,以下为 Request 信息:'. json_encode($request)); + } else { + // 保存日志 + $reqData = $request->getParsedBody(); + unset($reqData['apiData']); + $response = $event->getResult(); + $service->save([ + 'api_id' => $data['id'], + 'api_name' => $data['name'], + 'access_name' => $data['access_name'], + 'request_data' => [ 'data' => $reqData, 'params' => $request->getQueryParams() ], + 'response_code' => $response->getStatusCode(), +// 返回内容以免过大,暂不保存访问内容 +// 'response_data' => $response->getBody()->getContents(), + 'ip' => $request->ip(), + 'ip_location' => Str::ipToRegion($request->ip()), + 'access_time' => date('Y-m-d H:i:s') + ]); + } + } +} \ No newline at end of file diff --git a/api/Middleware/VerifyInterfaceMiddleware.php b/api/Middleware/VerifyInterfaceMiddleware.php new file mode 100644 index 0000000..d6fab70 --- /dev/null +++ b/api/Middleware/VerifyInterfaceMiddleware.php @@ -0,0 +1,196 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Api\Middleware; + +use App\System\Service\SystemAppService; +use Builder\Event\ApiAfter; +use Builder\Event\ApiBefore; +use App\System\Model\SystemApi; +use App\System\Service\SystemApiService; +use Hyperf\Di\Annotation\Inject; +use Hyperf\Context\Context; +use Builder\Exception\NormalStatusException; +use Builder\Helper\MineCode; +use Builder\MineRequest; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; + +class VerifyInterfaceMiddleware implements MiddlewareInterface +{ + /** + * 事件调度器 + * @var EventDispatcherInterface + */ + #[Inject] + protected EventDispatcherInterface $evDispatcher; + + /** + * 验证检查接口 + * @param ServerRequestInterface $request + * @param RequestHandlerInterface $handler + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler):ResponseInterface + { + $this->crossSetting($request); + + return $this->run($request, $handler); + } + + /** + * 跨域设置 + * @param $request + */ + protected function crossSetting($request): void + { + $crossData = [ + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Methods' => 'POST,GET,PUT,DELETE,OPTIONS', + 'Access-Control-Allow-Headers' => 'Version, Access-Token, User-Token, Api-Auth, User-Agent, Keep-Alive, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With', + 'Access-Control-Allow-Credentials' => 'true' + ]; + + foreach ($crossData as $name => $value) { + $request->withHeader($name, $value); + } + } + + /** + * 访问接口鉴权处理 + * @param ServerRequestInterface $request + * @return int + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + protected function auth(ServerRequestInterface $request): int + { + try { + /* @var $service SystemAppService */ + $service = container()->get(SystemAppService::class); + $queryParams = $request->getQueryParams(); + switch ($this->_getApiData()['auth_mode']) { + case SystemApi::AUTH_MODE_EASY: + if (empty($queryParams['app_id'])) { + return MineCode::API_APP_ID_MISSING; + } + if (empty($queryParams['identity'])) { + return MineCode::API_IDENTITY_MISSING; + } + return $service->verifyEasyMode($queryParams['app_id'], $queryParams['identity']); + case SystemApi::AUTH_MODE_NORMAL: + + if (empty($queryParams['access_token'])) { + return MineCode::API_ACCESS_TOKEN_MISSING; + } + return $service->verifyNormalMode($queryParams['access_token']); + default: + throw new \RuntimeException(); + } + } catch (\Throwable $e) { + throw new NormalStatusException(t('mineadmin.api_auth_exception'), MineCode::API_AUTH_EXCEPTION); + } + } + + /** + * API常规检查 + * @throws NotFoundExceptionInterface + * @throws ContainerExceptionInterface + */ + protected function apiModelCheck($request): ServerRequestInterface + { + $service = container()->get(SystemApiService::class); + $apiModel = $service->mapper->one(function($query) { + $request = container()->get(MineRequest::class); + $query->where('access_name', $request->route('method')); + }); + + // 检查接口是否存在 + if (! $apiModel) { + throw new NormalStatusException(t('mineadmin.not_found'), MineCode::NOT_FOUND); + } + + // 检查接口是否停用 + if ($apiModel['status'] == SystemApi::DISABLE) { + throw new NormalStatusException(t('mineadmin.api_stop'), MineCode::RESOURCE_STOP); + } + + // 检查接口请求方法 + if ($apiModel['request_mode'] !== SystemApi::METHOD_ALL && $request->getMethod()[0] !== $apiModel['request_mode']) { + throw new NormalStatusException( + t('mineadmin.not_allow_method', ['method' => $request->getMethod()]), + MineCode::METHOD_NOT_ALLOW + ); + } + + $this->_setApiData($apiModel->toArray()); + + // 合并入参 + return $request->withParsedBody(array_merge( + $request->getParsedBody(), ['apiData' => $apiModel->toArray()] + )); + } + + /** + * 运行 + * @param ServerRequestInterface $request + * @param RequestHandlerInterface $handler + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + protected function run(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $this->evDispatcher->dispatch(new ApiBefore()); + + $request = $this->apiModelCheck($request); + + if (($code = $this->auth($request)) !== MineCode::API_VERIFY_PASS) { + throw new NormalStatusException(t('mineadmin.api_auth_fail'), $code); + } + + $result = $handler->handle($request); + + $event = new ApiAfter($this->_getApiData(), $result); + $this->evDispatcher->dispatch($event); + + return $event->getResult(); + } + + /** + * 设置协程上下文 + * @param array $data + */ + private function _setApiData(array $data) + { + Context::set('apiData', $data); + } + + /** + * 获取协程上下文 + * @return array + */ + private function _getApiData(): array + { + return Context::get('apiData', []); + } +} \ No newline at end of file diff --git a/api/Middleware/VerifyParamsMiddleware.php b/api/Middleware/VerifyParamsMiddleware.php new file mode 100644 index 0000000..e5cca17 --- /dev/null +++ b/api/Middleware/VerifyParamsMiddleware.php @@ -0,0 +1,60 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Api\Middleware; + +use App\System\Model\SystemApi; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; + +class VerifyParamsMiddleware implements MiddlewareInterface +{ + /** + * 验证接口参数 + * @param ServerRequestInterface $request + * @param RequestHandlerInterface $handler + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler):ResponseInterface + { + $apiData = $request->getParsedBody()['apiData']; + $requestData = $this->getRequestData($request, $apiData); + + $columns = container()->get(\App\System\Service\SystemApiService::class) + ->getColumnListByApiId((string) $apiData['id'])['api_column']; + + + // todo... + + return $handler->handle($request); + } + + protected function getRequestData(ServerRequestInterface $request, &$apiData): array + { + $bodyData = $request->getParsedBody(); unset($bodyData['apiData']); + + if ($apiData['request_mode'] === SystemApi::METHOD_GET) { + $params = $request->getQueryParams(); + } else if ($apiData['request_mode'] === SystemApi::METHOD_ALL) { + $params = array_merge($request->getQueryParams(), $bodyData); + } else { + $params = &$bodyData; + } + + return $params; + } +} \ No newline at end of file diff --git a/app/Mall/Controller/IndexController.php b/app/Mall/Controller/IndexController.php new file mode 100644 index 0000000..b03db54 --- /dev/null +++ b/app/Mall/Controller/IndexController.php @@ -0,0 +1,38 @@ +setHomeUrl('index'); + $user=new UserEntity(); + $setting->setUser($user); + return View::make($setting); + } +} \ No newline at end of file diff --git a/app/Mall/config.json b/app/Mall/config.json new file mode 100644 index 0000000..836b461 --- /dev/null +++ b/app/Mall/config.json @@ -0,0 +1,9 @@ +{ + "name": "Mall", + "label": "商城", + "description": "多用户商城,进销存,多功能供应集采系统", + "installed": false, + "enabled": true, + "version": "1.0.0", + "order": 1 +} \ No newline at end of file diff --git a/app/System/Controller/Api/SystemApiColumnController.php b/app/System/Controller/Api/SystemApiColumnController.php new file mode 100644 index 0000000..88522fd --- /dev/null +++ b/app/System/Controller/Api/SystemApiColumnController.php @@ -0,0 +1,183 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:api:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增 + * @param SystemApiColumnRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:api:save"), OperationLog("新增接口参数")] + public function save(SystemApiColumnRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:api:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新 + * @param int $id + * @param SystemApiColumnRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:api:update"), OperationLog("更新接口参数")] + public function update(int $id, SystemApiColumnRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:api:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:api:realDelete"), OperationLog("真实删除接口参数")] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:api:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 字段导出 + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @return ResponseInterface + */ + #[PostMapping("export")] + public function export(): ResponseInterface + { + return $this->service->export($this->request->all(), \App\System\Dto\ApiColumnDto::class, '字段列表'); + } + + /** + * 字段导入 + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("import")] + public function import(): ResponseInterface + { + return $this->service->import(\App\System\Dto\ApiColumnDto::class) ? $this->success() : $this->error(); + } + + /** + * 下载导入模板 + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("downloadTemplate")] + public function downloadTemplate(): ResponseInterface + { + return (new MineCollection)->export(\App\System\Dto\ApiColumnDto::class, '模板下载', []); + } + + /** + * 更改状态 + * @param SystemApiColumnRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:api:update"), OperationLog("更改接口状态")] + public function changeStatus(SystemApiColumnRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Api/SystemApiController.php b/app/System/Controller/Api/SystemApiController.php new file mode 100644 index 0000000..cbf7eef --- /dev/null +++ b/app/System/Controller/Api/SystemApiController.php @@ -0,0 +1,158 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 获取模块列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getModuleList")] + public function getModuleList(): ResponseInterface + { + $this->mine->scanModule(); + return $this->success($this->mine->getModuleInfo()); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:api:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增 + * @param SystemApiRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:api:save"), OperationLog] + public function save(SystemApiRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:api:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新 + * @param int $id + * @param SystemApiRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:api:update"), OperationLog] + public function update(int $id, SystemApiRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:api:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:api:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:api:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改状态 + * @param SystemApiRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:api:update"), OperationLog] + public function changeStatus(SystemApiRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Api/SystemApiGroupController.php b/app/System/Controller/Api/SystemApiGroupController.php new file mode 100644 index 0000000..ce0097b --- /dev/null +++ b/app/System/Controller/Api/SystemApiGroupController.php @@ -0,0 +1,157 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 列表,无分页 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("list")] + public function list(): ResponseInterface + { + return $this->success($this->service->getList($this->request->all())); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:apiGroup:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增 + * @param SystemApiGroupRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:apiGroup:save"), OperationLog] + public function save(SystemApiGroupRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:apiGroup:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新 + * @param int $id + * @param SystemApiGroupRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:apiGroup:update"), OperationLog] + public function update(int $id, SystemApiGroupRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:apiGroup:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:apiGroup:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:apiGroup:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改状态 + * @param SystemApiGroupRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:apiGroup:update"), OperationLog] + public function changeStatus(SystemApiGroupRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/App/SystemAppController.php b/app/System/Controller/App/SystemAppController.php new file mode 100644 index 0000000..e76eb93 --- /dev/null +++ b/app/System/Controller/App/SystemAppController.php @@ -0,0 +1,196 @@ +success(['app_id' => $this->service->getAppId()]); + } + + /** + * 获取APP SECRET + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + #[GetMapping("getAppSecret")] + public function getAppSecret(): ResponseInterface + { + return $this->success(['app_secret' => $this->service->getAppSecret()]); + } + + /** + * 列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("index"), Permission("system:app, system:app:index")] + public function index(): ResponseInterface + { + return $this->success($this->service->getPageList($this->request->all())); + } + + /** + * 获取绑定接口列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getApiList")] + public function getApiList(): ResponseInterface + { + return $this->success($this->service->getApiList((int) $this->request->input('id', null))); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:app:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增 + * @param SystemAppRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:app:save"), OperationLog] + public function save(SystemAppRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:app:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新 + * @param int $id + * @param SystemAppRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:app:update"), OperationLog] + public function update(int $id, SystemAppRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 绑定接口 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("bind/{id}"), Permission("system:app:bind"), OperationLog] + public function bind(int $id): ResponseInterface + { + return $this->service->bind($id, $this->request->input('apiIds', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:app:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:app:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:app:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改状态 + * @param SystemAppRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:apiGroup:update"), OperationLog] + public function changeStatus(SystemAppRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/App/SystemAppGroupController.php b/app/System/Controller/App/SystemAppGroupController.php new file mode 100644 index 0000000..836a5d0 --- /dev/null +++ b/app/System/Controller/App/SystemAppGroupController.php @@ -0,0 +1,157 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 列表,无分页 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("list")] + public function list(): ResponseInterface + { + return $this->success($this->service->getList()); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:appGroup:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增 + * @param SystemAppGroupRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:appGroup:save"), OperationLog] + public function save(SystemAppGroupRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:appGroup:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新 + * @param int $id + * @param SystemAppGroupRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:appGroup:update"), OperationLog] + public function update(int $id, SystemAppGroupRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:appGroup:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:appGroup:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:appGroup:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改状态 + * @param SystemAppGroupRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:appGroup:update"), OperationLog] + public function changeStatus(SystemAppGroupRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/CommonController.php b/app/System/Controller/CommonController.php new file mode 100644 index 0000000..cce1382 --- /dev/null +++ b/app/System/Controller/CommonController.php @@ -0,0 +1,171 @@ +success($this->mine->getModuleInfo()); + } + + /** + * 获取用户列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getUserList")] + public function getUserList(): ResponseInterface + { + return $this->success($this->userService->getPageList($this->request->all())); + } + + /** + * 通过 id 列表获取用户基础信息 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("getUserInfoByIds")] + public function getUserInfoByIds(): ResponseInterface + { + return $this->success($this->userService->getUserInfoByIds((array) $this->request->input('ids', []))); + } + + /** + * 获取部门树列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getDeptTreeList")] + public function getDeptTreeList(): ResponseInterface + { + return $this->success($this->deptService->getSelectTree()); + } + + /** + * 获取角色列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getRoleList")] + public function getRoleList(): ResponseInterface + { + return $this->success($this->roleService->getList()); + } + + /** + * 获取岗位列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getPostList")] + public function getPostList(): ResponseInterface + { + return $this->success($this->postService->getList()); + } + + /** + * 获取公告列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getNoticeList")] + public function getNoticeList(): ResponseInterface + { + return $this->success($this->noticeService->getPageList($this->request->all())); + } + + /** + * 获取登录日志列表 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getLoginLogList")] + public function getLoginLogPageList(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->loginLogService->getPageList($this->request->all())); + } + + /** + * 获取操作日志列表 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getOperationLogList")] + public function getOperLogPageList(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->operLogService->getPageList($this->request->all())); + } + + /** + * 清除所有缓存 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("clearAllCache")] + public function clearAllCache(): ResponseInterface + { + $this->userService->clearCache((string) user()->getId()); + return $this->success(); + } +} \ No newline at end of file diff --git a/app/System/Controller/DataCenter/AttachmentController.php b/app/System/Controller/DataCenter/AttachmentController.php new file mode 100644 index 0000000..7295370 --- /dev/null +++ b/app/System/Controller/DataCenter/AttachmentController.php @@ -0,0 +1,89 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 回收站列表数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:attachment:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 单个或批量删除附件 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:attachment:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除文件 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:attachment:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的文件 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:attachment:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } +} diff --git a/app/System/Controller/DataCenter/DataMaintainController.php b/app/System/Controller/DataCenter/DataMaintainController.php new file mode 100644 index 0000000..9402a62 --- /dev/null +++ b/app/System/Controller/DataCenter/DataMaintainController.php @@ -0,0 +1,77 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 详情 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("detailed"), Permission("system:dataMaintain:detailed")] + public function detailed(): ResponseInterface + { + return $this->success($this->service->getColumnList($this->request->input('table', null))); + } + + /** + * 优化表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("optimize"), Permission("system:dataMaintain:optimize"), OperationLog] + public function optimize(): ResponseInterface + { + $tables = $this->request->input('tables', []); + return $this->service->optimize($tables) ? $this->success() : $this->error(); + } + + /** + * 清理表碎片 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("fragment"), Permission("system:dataMaintain:fragment"), OperationLog] + public function fragment(): ResponseInterface + { + $tables = $this->request->input('tables', []); + return $this->service->fragment($tables) ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/DataCenter/DictDataController.php b/app/System/Controller/DataCenter/DictDataController.php new file mode 100644 index 0000000..80109df --- /dev/null +++ b/app/System/Controller/DataCenter/DictDataController.php @@ -0,0 +1,196 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 快捷查询一个字典 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("list")] + public function list(): ResponseInterface + { + return $this->success($this->service->getList($this->request->all())); + } + + /** + * 快捷查询多个字典 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("lists")] + public function lists(): ResponseInterface + { + return $this->success($this->service->getLists($this->request->all())); + } + + /** + * 清除字典缓存 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("clearCache"), Permission("system:dict:clearCache"), OperationLog] + public function clearCache(): ResponseInterface + { + return $this->service->clearCache() ? $this->success() : $this->error(); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:dict:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增字典类型 + * @param SystemDictDataRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:dict:save"), OperationLog] + public function save(SystemDictDataRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 获取一个字典类型数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:dict:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新一个字典类型 + * @param int $id + * @param SystemDictDataRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:dict:update"), OperationLog] + public function update(int $id, SystemDictDataRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量字典数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:dict:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除字典 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:dict:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的字典 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:dict:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改字典状态 + * @param SystemDictDataRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:dict:update"), OperationLog] + public function changeStatus(SystemDictDataRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $request->input('id'), (string) $request->input('status')) + ? $this->success() : $this->error(); + } + + /** + * 数字运算操作 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("numberOperation"), Permission("system:dict:update"), OperationLog] + public function numberOperation(): ResponseInterface + { + return $this->service->numberOperation( + (int) $this->request->input('id'), + (string) $this->request->input('numberName'), + (int) $this->request->input('numberValue', 1), + ) ? $this->success() : $this->error(); + } +} diff --git a/app/System/Controller/DataCenter/DictTypeController.php b/app/System/Controller/DataCenter/DictTypeController.php new file mode 100644 index 0000000..1127031 --- /dev/null +++ b/app/System/Controller/DataCenter/DictTypeController.php @@ -0,0 +1,148 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:dict:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增字典类型 + * @param SystemDictTypeRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:dict:save"), OperationLog("新增字典类型")] + public function save(SystemDictTypeRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 获取一个字典类型数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:dict:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新一个字典类型 + * @param int $id + * @param SystemDictTypeRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:dict:update"), OperationLog("更新字典类型")] + public function update(int $id, SystemDictTypeRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量字典数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:dict:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除字典数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:dict:realDelete"), OperationLog("删除字典类型")] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的用户 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:dict:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改字典类型状态 + * @param SystemDictTypeRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:dict:update"), OperationLog("修改字典类型状态")] + public function changeStatus(SystemDictTypeRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} diff --git a/app/System/Controller/DataCenter/NoticeController.php b/app/System/Controller/DataCenter/NoticeController.php new file mode 100644 index 0000000..88a7234 --- /dev/null +++ b/app/System/Controller/DataCenter/NoticeController.php @@ -0,0 +1,130 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:notice:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增 + * @param SystemNoticeRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + #[PostMapping("save"), Permission("system:notice:save"), OperationLog] + public function save(SystemNoticeRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:notice:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新 + * @param int $id + * @param SystemNoticeRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:notice:update"), OperationLog] + public function update(int $id, SystemNoticeRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:notice:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:notice:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:notice:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } +} diff --git a/app/System/Controller/DataCenter/QueueMessageController.php b/app/System/Controller/DataCenter/QueueMessageController.php new file mode 100644 index 0000000..c50a6bf --- /dev/null +++ b/app/System/Controller/DataCenter/QueueMessageController.php @@ -0,0 +1,106 @@ +success($this->service->getReceiveMessage($this->request->all())); + } + + /** + * 已发送消息列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("sendList")] + public function sendList(): ResponseInterface + { + return $this->success($this->service->getSendMessage($this->request->all())); + } + + /** + * 发私信 + * @param MessageRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + #[PostMapping("sendPrivateMessage")] + public function sendPrivateMessage(MessageRequest $request): ResponseInterface + { + return $this->service->sendPrivateMessage($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 获取接收人列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getReceiveUser")] + public function getReceiveUser(): ResponseInterface + { + return $this->success( + $this->service->getReceiveUserList( + (int) $this->request->input('id', 0), + $this->request->all() + ) + ); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("deletes")] + public function deletes(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更新状态到已读 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("updateReadStatus")] + public function updateReadStatus(): ResponseInterface + { + return $this->service->updateDataStatus((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } +} diff --git a/app/System/Controller/IndexController.php b/app/System/Controller/IndexController.php new file mode 100644 index 0000000..159a81e --- /dev/null +++ b/app/System/Controller/IndexController.php @@ -0,0 +1,38 @@ +setHomeUrl('index'); + $user=new UserEntity(); + $setting->setUser($user); + return View::make($setting); + } + +} \ No newline at end of file diff --git a/app/System/Controller/LoginController.php b/app/System/Controller/LoginController.php new file mode 100644 index 0000000..16165c5 --- /dev/null +++ b/app/System/Controller/LoginController.php @@ -0,0 +1,94 @@ +validated(); + $vo = new UserServiceVo(); + $vo->setUsername($requestData['username']); + $vo->setPassword($requestData['password']); + return $this->success(['token' => $this->userService->login($vo)]); + } + + /** + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + #[PostMapping("logout"), Auth] + public function logout(): ResponseInterface + { + $this->userService->logout(); + return $this->success(); + } + + /** + * 用户信息 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getInfo"), Auth] + public function getInfo(): ResponseInterface + { + return $this->success($this->systemUserService->getInfo()); + } + + /** + * 刷新token + * @param LoginUser $user + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + #[PostMapping("refresh")] + public function refresh(LoginUser $user): ResponseInterface + { + return $this->success(['token' => $user->refresh()]); + } +} \ No newline at end of file diff --git a/app/System/Controller/Logs/LogsController.php b/app/System/Controller/Logs/LogsController.php new file mode 100644 index 0000000..ff0ac76 --- /dev/null +++ b/app/System/Controller/Logs/LogsController.php @@ -0,0 +1,146 @@ +success($this->loginLogService->getPageList($this->request->all())); + } + + /** + * 获取操作日志列表 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getOperLogPageList"), Permission("system:operLog")] + public function getOperLogPageList(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->operLogService->getPageList($this->request->all())); + } + + /** + * 获取接口日志列表 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getApiLogPageList"), Permission("system:apiLog")] + public function getApiLogPageList(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->apiLogService->getPageList($this->request->all())); + } + + /** + * 获取队列日志列表 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getQueueLogPageList"), Permission("system:queueLog")] + public function getQueueLogPageList(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->queueLogService->getPageList($this->request->all())); + } + + /** + * 删除队列日志 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("deleteQueueLog"), Permission("system:queueLog:delete"), OperationLog] + public function deleteQueueLog(): \Psr\Http\Message\ResponseInterface + { + return $this->queueLogService->delete((array)$this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 删除操作日志 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("deleteOperLog"), Permission("system:operLog:delete"), OperationLog] + public function deleteOperLog(): \Psr\Http\Message\ResponseInterface + { + return $this->operLogService->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 删除登录日志 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("deleteLoginLog"), Permission("system:loginLog:delete"), OperationLog] + public function deleteLoginLog(): \Psr\Http\Message\ResponseInterface + { + return $this->loginLogService->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 删除API访问日志 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("deleteApiLog"), Permission("system:apiLog:delete"), OperationLog] + public function deleteApiLog(): \Psr\Http\Message\ResponseInterface + { + return $this->apiLogService->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } +} diff --git a/app/System/Controller/Modules/ModuleController.php b/app/System/Controller/Modules/ModuleController.php new file mode 100644 index 0000000..9ec96b2 --- /dev/null +++ b/app/System/Controller/Modules/ModuleController.php @@ -0,0 +1,97 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 新增本地模块 + * @param ModuleRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("save"), Permission("setting:module:save"), OperationLog] + public function save(ModuleRequest $request): ResponseInterface + { + $this->service->createModule($request->validated()); + return $this->success(); + } + + /** + * 启停用模块 + * @param ModuleRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("modifyStatus"), Permission("setting:module:status"), OperationLog] + public function modifyStatus(ModuleRequest $request): ResponseInterface + { + return $this->service->modifyStatus($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 安装模块 + * @param string $name + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("install"), Permission("setting:module:install"), OperationLog] + public function install(): ResponseInterface + { + return $this->service->installModuleData($this->request->input('name')) ? $this->success() : $this->error(); + } + + /** + * 卸载删除模块 + * @param string $name + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + #[DeleteMapping("delete"), Permission("setting:module:delete"), OperationLog] + public function delete(): ResponseInterface + { + return $this->service->uninstallModule($this->request->input('name')) ? $this->success() : $this->error(); + } + +} \ No newline at end of file diff --git a/app/System/Controller/Monitor/CacheMonitorController.php b/app/System/Controller/Monitor/CacheMonitorController.php new file mode 100644 index 0000000..c8679a7 --- /dev/null +++ b/app/System/Controller/Monitor/CacheMonitorController.php @@ -0,0 +1,78 @@ +success($this->service->getCacheServerInfo()); + } + + /** + * 查看key内容 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("view")] + public function view(): ResponseInterface + { + return $this->success(['content' => $this->service->view($this->request->input('key'))]); + } + + /** + * 删除一个缓存 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:cache:delete"), OperationLog] + public function delete(): ResponseInterface + { + return $this->service->delete($this->request->input('key', null)) + ? $this->success() + : $this->error(); + } + + /** + * 清空所有缓存 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("clear"), Permission("system:cache:clear"), OperationLog] + public function clear(): ResponseInterface + { + return $this->service->clear() ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Monitor/OnlineUserMonitorController.php b/app/System/Controller/Monitor/OnlineUserMonitorController.php new file mode 100644 index 0000000..2bacb69 --- /dev/null +++ b/app/System/Controller/Monitor/OnlineUserMonitorController.php @@ -0,0 +1,51 @@ +success($this->service->getOnlineUserPageList($this->request->all())); + } + + /** + * 强退用户 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + #[PostMapping("kick"), Permission("system:onlineUser:kick")] + public function kickUser(): \Psr\Http\Message\ResponseInterface + { + return $this->service->kickUser((string) $this->request->input('id')) ? + $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Monitor/ServerMonitorController.php b/app/System/Controller/Monitor/ServerMonitorController.php new file mode 100644 index 0000000..f8cc1b8 --- /dev/null +++ b/app/System/Controller/Monitor/ServerMonitorController.php @@ -0,0 +1,41 @@ +success([ + 'cpu' => $this->service->getCpuInfo(), + 'memory' => $this->service->getMemInfo(), + 'phpenv' => $this->service->getPhpAndEnvInfo(), + 'disk' => $this->service->getDiskInfo() + ]); + } +} \ No newline at end of file diff --git a/app/System/Controller/Permission/DeptController.php b/app/System/Controller/Permission/DeptController.php new file mode 100644 index 0000000..0203b12 --- /dev/null +++ b/app/System/Controller/Permission/DeptController.php @@ -0,0 +1,195 @@ +success($this->service->getTreeList($this->request->all())); + } + + /** + * 回收站部门树列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:dept:recycle")] + public function recycleTree():ResponseInterface + { + return $this->success($this->service->getTreeListByRecycle($this->request->all())); + } + + /** + * 前端选择树(不需要权限) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("tree")] + public function tree(): ResponseInterface + { + return $this->success($this->service->getSelectTree()); + } + + #[GetMapping("getLeaderList"), Permission("system:dept, system:dept:index")] + public function getLeaderList() + { + return $this->success($this->service->getLeaderList($this->request->all())); + } + + /** + * 新增部门 + * @param SystemDeptRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:dept:save"), OperationLog] + public function save(SystemDeptRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 新增部门领导 + * @param SystemDeptRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("addLeader"), Permission("system:dept:update"), OperationLog("新增部门领导")] + public function addLeader(SystemDeptRequest $request): ResponseInterface + { + return $this->service->addLeader($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 删除部门领导 + * @param SystemDeptRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[DeleteMapping("delLeader"), Permission("system:dept:delete"), OperationLog("删除部门领导")] + public function delLeader(): ResponseInterface + { + return $this->service->delLeader($this->request->all()) ? $this->success() : $this->error(); + } + + /** + * 更新部门 + * @param int $id + * @param SystemDeptRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:dept:update"), OperationLog] + public function update(int $id, SystemDeptRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除部门到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:dept:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除部门 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:dept:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + $data = $this->service->realDel((array) $this->request->input('ids', [])); + return is_null($data) ? + $this->success() : + $this->success(t('system.exists_children_ctu', ['names' => implode(',', $data)])); + } + + /** + * 单个或批量恢复在回收站的部门 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:dept:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改部门状态 + * @param SystemDeptRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:dept:changeStatus"), OperationLog] + public function changeStatus(SystemDeptRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $request->input('id'), (string) $request->input('status')) + ? $this->success() : $this->error(); + } + + /** + * 数字运算操作 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("numberOperation"), Permission("system:dept:update"), OperationLog] + public function numberOperation(): ResponseInterface + { + return $this->service->numberOperation( + (int) $this->request->input('id'), + (string) $this->request->input('numberName'), + (int) $this->request->input('numberValue', 1), + ) ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Permission/MenuController.php b/app/System/Controller/Permission/MenuController.php new file mode 100644 index 0000000..c0e64c1 --- /dev/null +++ b/app/System/Controller/Permission/MenuController.php @@ -0,0 +1,163 @@ +success($this->service->getTreeList($this->request->all())); + } + + /** + * 回收站菜单树列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:menu:recycle")] + public function recycle():ResponseInterface + { + return $this->success($this->service->getTreeListByRecycle($this->request->all())); + } + + /** + * 前端选择树(不需要权限) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("tree")] + public function tree(): ResponseInterface + { + return $this->success($this->service->getSelectTree($this->request->all())); + } + + /** + * 新增菜单 + * @param SystemMenuRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:menu:save"), OperationLog] + public function save(SystemMenuRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 更新菜单 + * @param int $id + * @param SystemMenuRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:menu:update"), OperationLog] + public function update(int $id, SystemMenuRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) + ? $this->success() : $this->error(t('mineadmin.data_no_change')); + } + + /** + * 单个或批量删除菜单到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:menu:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除菜单 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:menu:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + $menus = $this->service->realDel((array) $this->request->input('ids', [])); + return is_null($menus) ? + $this->success() : + $this->success(t('system.exists_children_ctu', ['names' => implode(',', $menus)])); + } + + /** + * 单个或批量恢复在回收站的菜单 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:menu:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改菜单状态 + * @param SystemMenuRequest $request + * @return ResponseInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:menu:update"), OperationLog] + public function changeStatus(SystemMenuRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } + + /** + * 数字运算操作 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("numberOperation"), Permission("system:menu:update"), OperationLog] + public function numberOperation(): ResponseInterface + { + return $this->service->numberOperation( + (int) $this->request->input('id'), + (string) $this->request->input('numberName'), + (int) $this->request->input('numberValue', 1), + ) ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Permission/PostController.php b/app/System/Controller/Permission/PostController.php new file mode 100644 index 0000000..7cb0f64 --- /dev/null +++ b/app/System/Controller/Permission/PostController.php @@ -0,0 +1,171 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 岗位回收站分页列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:post:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 获取岗位列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("list")] + public function list(): ResponseInterface + { + return $this->success($this->service->getList()); + } + + /** + * 保存数据 + * @param SystemPostRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:post:save"), OperationLog] + public function save(SystemPostRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 获取一条数据信息 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:post:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新数据 + * @param int $id + * @param SystemPostRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:post:update"), OperationLog] + public function update(int $id, SystemPostRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:post:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:post:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:post:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改岗位状态 + * @param SystemPostRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:post:changeStatus"), OperationLog] + public function changeStatus(SystemPostRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $request->input('id'), (string) $request->input('status')) + ? $this->success() : $this->error(); + } + + /** + * 数字运算操作 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("numberOperation"), Permission("system:post:update"), OperationLog] + public function numberOperation(): ResponseInterface + { + return $this->service->numberOperation( + (int) $this->request->input('id'), + (string) $this->request->input('numberName'), + (int) $this->request->input('numberValue', 1), + ) ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Permission/RoleController.php b/app/System/Controller/Permission/RoleController.php new file mode 100644 index 0000000..5e0df29 --- /dev/null +++ b/app/System/Controller/Permission/RoleController.php @@ -0,0 +1,210 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 回收站角色分页列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:role:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 通过角色获取菜单 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getMenuByRole/{id}")] + public function getMenuByRole(int $id): ResponseInterface + { + return $this->success($this->service->getMenuByRole($id)); + } + + /** + * 通过角色获取部门 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getDeptByRole/{id}")] + public function getDeptByRole(int $id): ResponseInterface + { + return $this->success($this->service->getDeptByRole($id)); + } + + /** + * 获取角色列表 (不验证权限) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("list")] + public function list(): ResponseInterface + { + return $this->success($this->service->getList()); + } + + /** + * 新增角色 + * @param SystemRoleRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:role:save"), OperationLog] + public function save(SystemRoleRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 更新角色 + * @param int $id + * @param SystemRoleRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:role:update"), OperationLog] + public function update(int $id, SystemRoleRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 更新用户菜单权限 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("menuPermission/{id}"), Permission("system:role:menuPermission"), OperationLog] + public function menuPermission(int $id): ResponseInterface + { + return $this->service->update($id, $this->request->all()) ? $this->success() : $this->error(); + } + + /** + * 更新用户数据权限 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("dataPermission/{id}"), Permission("system:role:dataPermission"), OperationLog] + public function dataPermission(int $id): ResponseInterface + { + return $this->service->update($id, $this->request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:role:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:role:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:role:recovery")] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改角色状态 + * @param SystemRoleRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:role:changeStatus"), OperationLog] + public function changeStatus(SystemRoleRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $request->input('id'), (string) $request->input('status')) + ? $this->success() : $this->error(); + } + + /** + * 数字运算操作 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("numberOperation"), Permission("system:role:update"), OperationLog] + public function numberOperation(): ResponseInterface + { + return $this->service->numberOperation( + (int) $this->request->input('id'), + (string) $this->request->input('numberName'), + (int) $this->request->input('numberValue', 1), + ) ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Permission/UserController.php b/app/System/Controller/Permission/UserController.php new file mode 100644 index 0000000..bc58a2a --- /dev/null +++ b/app/System/Controller/Permission/UserController.php @@ -0,0 +1,246 @@ +success($this->service->getPageList($this->request->all(), false)); + } + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("system:user:recycle")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } + + /** + * 新增一个用户 + * @param SystemUserRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("system:user:save"), OperationLog] + public function save(SystemUserRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 获取一个用户信息 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("system:user:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新一个用户信息 + * @param int $id + * @param SystemUserRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("system:user:update"), OperationLog] + public function update(int $id, SystemUserRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除用户到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("system:user:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量真实删除用户 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("system:user:realDelete"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 单个或批量恢复在回收站的用户 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("system:user:recovery"), OperationLog] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改用户状态 + * @param SystemUserRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("system:user:changeStatus"), OperationLog] + public function changeStatus(SystemUserRequest $request): ResponseInterface + { + return $this->service->changeStatus((int) $request->input('id'), (string) $request->input('status')) + ? $this->success() : $this->error(); + } + + /** + * 清除用户缓存 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("clearCache"), Permission("system:user:cache")] + public function clearCache(): ResponseInterface + { + $this->service->clearCache((string) $this->request->input('id', null)); + return $this->success(); + } + + /** + * 设置用户首页 + * @param SystemUserRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("setHomePage"), Permission("system:user:homePage")] + public function setHomePage(SystemUserRequest $request): ResponseInterface + { + return $this->service->setHomePage($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 初始化用户密码 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("initUserPassword"), Permission("system:user:initUserPassword"), OperationLog] + public function initUserPassword(): ResponseInterface + { + return $this->service->initUserPassword((int) $this->request->input('id')) ? $this->success() : $this->error(); + } + + /** + * 更改用户资料,含修改头像 (不验证权限) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("updateInfo")] + public function updateInfo(): ResponseInterface + { + return $this->service->updateInfo($this->request->all()) ? $this->success() : $this->error(); + } + + /** + * 修改密码 (不验证权限) + * @param SystemUserRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("modifyPassword")] + public function modifyPassword(SystemUserRequest $request): ResponseInterface + { + return $this->service->modifyPassword($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 用户导出 + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("export"), Permission("system:user:export"), OperationLog] + public function export(): ResponseInterface + { + return $this->service->export($this->request->all(), \App\System\Dto\UserDto::class, '用户列表'); + } + + /** + * 用户导入 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + */ + #[PostMapping("import"), Permission("system:user:import")] + public function import(): ResponseInterface + { + return $this->service->import(\App\System\Dto\UserDto::class) ? $this->success() : $this->error(); + } + + /** + * 下载导入模板 + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("downloadTemplate")] + public function downloadTemplate(): ResponseInterface + { + return (new MineCollection)->export(\App\System\Dto\UserDto::class, '模板下载', []); + } +} \ No newline at end of file diff --git a/app/System/Controller/ServerController.php b/app/System/Controller/ServerController.php new file mode 100644 index 0000000..5e67d5d --- /dev/null +++ b/app/System/Controller/ServerController.php @@ -0,0 +1,93 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace App\System\Controller; + +use App\System\Service\SystemQueueMessageService; +use Hyperf\Contract\OnCloseInterface; +use Hyperf\Contract\OnMessageInterface; +use Hyperf\Contract\OnOpenInterface; +use Psr\Http\Message\ServerRequestInterface; +use Swoole\Http\Request; +use Swoole\Http\Response; +use Swoole\WebSocket\Frame; +use Swoole\WebSocket\Server; + +/** + * Class ServerController + * @package App\System\Controller + */ +class ServerController implements OnMessageInterface, OnOpenInterface, OnCloseInterface +{ + /** + * @var int + */ + protected $uid; + + /** + * 成功连接到 ws 回调 + * @param Response|Server $server + * @param Request $request + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function onOpen($server, $request): void + { + $this->uid = user()->getUserInfo( + container()->get(ServerRequestInterface::class)->getQueryParams()['token'] + )['id']; + + console()->info( + "WebSocket [ user connection to message server: id > {$this->uid}, ". + "fd > {$request->fd}, time > ". date('Y-m-d H:i:s') .' ]' + ); + } + + /** + * 消息回调 + * @param Response|Server $server + * @param Frame $frame + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function onMessage($server, $frame): void + { + $data = json_decode($frame->data, true); + switch($data['event']) { + case 'get_unread_message': + $service = container()->get(SystemQueueMessageService::class); + $server->push($frame->fd, json_encode([ + 'event' => 'ev_new_message', + 'message' => 'success', + 'data' => $service->getUnreadMessage($this->uid)['items'] + ])); + break; + } + } + + /** + * 关闭 ws 连接回调 + * @param Response|\Swoole\Server $server + * @param int $fd + * @param int $reactorId + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function onClose($server, int $fd, int $reactorId): void + { + console()->info( + "WebSocket [ user close connect for message server: id > {$this->uid}, ". + "fd > {$fd}, time > ". date('Y-m-d H:i:s') .' ]' + ); + } +} \ No newline at end of file diff --git a/app/System/Controller/Settings/SystemConfigController.php b/app/System/Controller/Settings/SystemConfigController.php new file mode 100644 index 0000000..f0c80fa --- /dev/null +++ b/app/System/Controller/Settings/SystemConfigController.php @@ -0,0 +1,105 @@ +success($this->service->getList($this->request->all())); + } + + /** + * 保存配置 + * @param SettingConfigRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("setting:config:save"), OperationLog] + public function save(SettingConfigRequest $request): \Psr\Http\Message\ResponseInterface + { + return $this->service->save($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 更新配置 + * @param SettingConfigRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("update"), Permission("setting:config:update"), OperationLog] + public function update(SettingConfigRequest $request): \Psr\Http\Message\ResponseInterface + { + return $this->service->updated($this->request->input('key'), $request->validated()) ? $this->success() : $this->error(); + } + + /** + * 按 keys 更新配置 + * @param SettingConfigRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("updateByKeys"), Permission("setting:config:update"), OperationLog] + public function updateByKeys(): \Psr\Http\Message\ResponseInterface + { + return $this->service->updatedByKeys($this->request->all()) ? $this->success() : $this->error(); + } + + /** + * 删除配置 + * @param string $key + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("setting:config:delete"), OperationLog] + public function delete(): \Psr\Http\Message\ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 清除配置缓存 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("clearCache"), Permission("setting:config:clearCache"), OperationLog] + public function clearCache(): \Psr\Http\Message\ResponseInterface + { + return $this->service->clearCache() ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Settings/SystemConfigGroupController.php b/app/System/Controller/Settings/SystemConfigGroupController.php new file mode 100644 index 0000000..4742c9c --- /dev/null +++ b/app/System/Controller/Settings/SystemConfigGroupController.php @@ -0,0 +1,80 @@ +success($this->service->getList()); + } + + /** + * 保存配置组 + * @param SettingConfigGroupRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("setting:config:save"), OperationLog("保存配置组")] + public function save(SettingConfigGroupRequest $request): \Psr\Http\Message\ResponseInterface + { + return $this->service->save($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 更新配置组 + * @param SettingConfigGroupRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("update"), Permission("setting:config:update"), OperationLog("更新配置组")] + public function update(SettingConfigGroupRequest $request): \Psr\Http\Message\ResponseInterface + { + return $this->service->update((int) $this->request->input('id'), $request->validated()) ? $this->success() : $this->error(); + } + + /** + * 删除配置组 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("setting:config:delete"), OperationLog("删除配置组")] + public function delete(): \Psr\Http\Message\ResponseInterface + { + return $this->service->deleteConfigGroup((int) $this->request->input('id')) ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Tools/CrontabController.php b/app/System/Controller/Tools/CrontabController.php new file mode 100644 index 0000000..3f6ba5a --- /dev/null +++ b/app/System/Controller/Tools/CrontabController.php @@ -0,0 +1,158 @@ +success($this->service->getPageList($this->request->all())); + } + + /** + * 获取日志列表分页数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("logPageList")] + public function logPageList(): ResponseInterface + { + return $this->success($this->logService->getPageList($this->request->all())); + } + + /** + * 保存数据 + * @param SettingCrontabRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("setting:crontab:save"), OperationLog] + public function save(SettingCrontabRequest $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } + + /** + * 立即执行定时任务 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("run"), Permission("setting:crontab:run"), OperationLog] + public function run(): ResponseInterface + { + $id = $this->request->input('id', null); + if (is_null($id)) { + return $this->error(); + } else { + return $this->service->run($id) ? $this->success() : $this->error(); + } + } + + /** + * 获取一条数据信息 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("setting:crontab:read")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } + + /** + * 更新数据 + * @param int $id + * @param SettingCrontabRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("setting:crontab:update"), OperationLog] + public function update(int $id, SettingCrontabRequest $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } + + /** + * 单个或批量删除 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("setting:crontab:delete")] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 删除定时任务日志 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("deleteCrontabLog"), Permission("setting:crontab:deleteCrontabLog"), OperationLog("删除定时任务日志")] + public function deleteCrontabLog(): \Psr\Http\Message\ResponseInterface + { + return $this->logService->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 更改状态 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("setting:crontab:update"), OperationLog] + public function changeStatus(): ResponseInterface + { + return $this->service->changeStatus((int) $this->request->input('id'), (string) $this->request->input('status')) + ? $this->success() : $this->error(); + } +} \ No newline at end of file diff --git a/app/System/Controller/Tools/GenerateCodeController.php b/app/System/Controller/Tools/GenerateCodeController.php new file mode 100644 index 0000000..6d68be4 --- /dev/null +++ b/app/System/Controller/Tools/GenerateCodeController.php @@ -0,0 +1,169 @@ +success($this->tableService->getPageList($this->request->All())); + } + + /** + * 获取业务表字段信息 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getTableColumns")] + public function getTableColumns(): ResponseInterface + { + return $this->success($this->columnService->getList($this->request->all())); + } + + /** + * 预览代码 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + #[GetMapping("preview"), Permission("setting:code:preview")] + public function preview(): ResponseInterface + { + return $this->success($this->tableService->preview((int) $this->request->input('id', 0))); + } + + /** + * 读取表数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("readTable")] + public function readTable(): ResponseInterface + { + return $this->success($this->tableService->read((int) $this->request->input('id'))); + } + + /** + * 更新业务表信息 + * @param GenerateRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("update"), Permission("setting:code:update")] + public function update(GenerateRequest $request): ResponseInterface + { + return $this->tableService->updateTableAndColumns($request->validated()) ? $this->success() : $this->error(); + } + + /** + * 生成代码 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("generate"), Permission("setting:code:generate"), OperationLog] + public function generate(): ResponseInterface + { + return $this->_download( + $this->tableService->generate((array) $this->request->input('ids', [])), + 'mineadmin.zip' + ); + } + + /** + * 加载数据表 + * @param GenerateRequest $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("loadTable"), Permission("setting:code:loadTable"), OperationLog] + public function loadTable(GenerateRequest $request): ResponseInterface + { + return $this->tableService->loadTable($request->input('names')) ? $this->success() : $this->error(); + } + + /** + * 删除代码生成表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("setting:code:delete"), OperationLog] + public function delete(): ResponseInterface + { + return $this->tableService->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } + + /** + * 同步数据库中的表信息跟字段 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("sync/{id}"), Permission("setting:code:sync"), OperationLog] + public function sync(int $id): ResponseInterface + { + return $this->tableService->sync($id) ? $this->success() : $this->error(); + } + + /** + * 获取所有启用状态模块下的所有模型 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getModels")] + public function getModels(): ResponseInterface + { + return $this->success($this->tableService->getModels()); + } +} \ No newline at end of file diff --git a/app/System/Controller/UploadController.php b/app/System/Controller/UploadController.php new file mode 100644 index 0000000..dbb4e39 --- /dev/null +++ b/app/System/Controller/UploadController.php @@ -0,0 +1,171 @@ +validated() && $request->file('file')->isValid()) { + $data = $this->service->upload( + $request->file('file'), $request->all() + ); + return empty($data) ? $this->error() : $this->success($data); + } else { + return $this->error(t('system.upload_file_verification_fail')); + } + } + + /** + * 上传图片 + * @param UploadRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \League\Flysystem\FileExistsException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("uploadImage"), Auth] + public function uploadImage(UploadRequest $request): \Psr\Http\Message\ResponseInterface + { + if ($request->validated() && $request->file('image')->isValid()) { + $data = $this->service->upload( + $request->file('image'), $request->all() + ); + return empty($data) ? $this->error() : $this->success($data); + } else { + return $this->error(t('system.upload_image_verification_fail')); + } + } + + /** + * 分块上传 + * @param UploadRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("chunkUpload"), Auth] + public function chunkUpload(UploadRequest $request): \Psr\Http\Message\ResponseInterface + { + return ($data = $this->service->chunkUpload($request->validated())) ? $this->success($data) : $this->error(); + } + + /** + * 保存网络图片 + * @param UploadRequest $request + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + #[PostMapping("saveNetworkImage"), Auth] + public function saveNetworkImage(UploadRequest $request): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->service->saveNetworkImage($request->validated())); + } + + /** + * 获取当前目录所有文件和目录 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getAllFiles"), Auth] + public function getAllFile(): \Psr\Http\Message\ResponseInterface + { + return $this->success( + $this->service->getAllFile($this->request->all()) + ); + } + + /** + * 通过ID获取文件信息 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getFileInfoById")] + public function getFileInfoByid(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->service->read((int) $this->request->input('id', null))); + } + + /** + * 通过HASH获取文件信息 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("getFileInfoByHash")] + public function getFileInfoByHash(): \Psr\Http\Message\ResponseInterface + { + return $this->success($this->service->readByHash($this->request->input('hash', null))); + } + + /** + * 根据id下载文件 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("downloadById")] + public function downloadById(): \Psr\Http\Message\ResponseInterface + { + $id = $this->request->input('id'); + if (empty($id)) { + return $this->error("附件ID必填"); + } + $model = $this->service->read((int) $id); + if (! $model) { + throw new \Builder\Exception\MineException('附件不存在', 500); + } + return $this->_download(BASE_PATH . '/public' . $model->url, $model->origin_name); + } + + /** + * 根据hash下载文件 + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("downloadByHash")] + public function downloadByHash(): \Psr\Http\Message\ResponseInterface + { + $hash = $this->request->input('hash'); + if (empty($hash)) { + return $this->error("附件hash必填"); + } + $model = $this->service->readByHash($hash); + if (! $model) { + throw new \Builder\Exception\MineException('附件不存在', 500); + } + return $this->_download(BASE_PATH . '/public' . $model->url, $model->origin_name); + } +} \ No newline at end of file diff --git a/app/System/Crontab/ClearLogCrontab.php b/app/System/Crontab/ClearLogCrontab.php new file mode 100644 index 0000000..d2c3abf --- /dev/null +++ b/app/System/Crontab/ClearLogCrontab.php @@ -0,0 +1,28 @@ +engine = 'Innodb'; + $table->comment('用户信息表'); + $table->bigIncrements('id')->comment('用户ID,主键'); + $table->addColumn('string', 'username', ['length' => 20, 'comment' => '用户名']); + $table->addColumn('string', 'password', ['length' => 100, 'comment' => '密码']); + $table->addColumn('string', 'user_type', ['length' => 3, 'comment' => '用户类型:(100系统用户)', 'default' => '100'])->nullable(); + $table->addColumn('string', 'nickname', ['length' => 30, 'comment' => '用户昵称'])->nullable(); + $table->addColumn('string', 'phone', ['length' => 11, 'comment' => '手机'])->nullable(); + $table->addColumn('string', 'email', ['length' => 50, 'comment' => '用户邮箱'])->nullable(); + $table->addColumn('string', 'avatar', ['length' => 255, 'comment' => '用户头像'])->nullable(); + $table->addColumn('string', 'signed', ['length' => 255, 'comment' => '个人签名'])->nullable(); + $table->addColumn('string', 'dashboard', ['length' => 100, 'comment' => '后台首页类型'])->nullable(); + $table->addColumn('bigInteger', 'dept_id', ['unsigned' => true, 'comment' => '部门ID'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('ipAddress', 'login_ip', ['comment' => '最后登陆IP'])->nullable(); + $table->addColumn('timestamp', 'login_time', ['comment' => '最后登陆时间'])->nullable(); + $table->addColumn('string', 'backend_setting', ['length' => 500, 'comment' => '后台设置数据'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->unique('username'); + $table->index('dept_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_user'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_215320_create_system_menu_table.php b/app/System/Database/Migrations/2021_04_18_215320_create_system_menu_table.php new file mode 100644 index 0000000..132c034 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_215320_create_system_menu_table.php @@ -0,0 +1,55 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemMenuTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_menu', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('菜单信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'parent_id', ['unsigned' => true, 'comment' => '父ID']); + $table->addColumn('string', 'level', ['length' => 500, 'comment' => '组级集合']); + $table->addColumn('string', 'name', ['length' => 50, 'comment' => '菜单名称']); + $table->addColumn('string', 'code', ['length' => 100, 'comment' => '菜单标识代码']); + $table->addColumn('string', 'icon', ['length' => 50, 'comment' => '菜单图标'])->nullable(); + $table->addColumn('string', 'route', ['length' => 200, 'comment' => '路由地址'])->nullable(); + $table->addColumn('string', 'component', ['length' => 255, 'comment' => '组件路径'])->nullable(); + $table->addColumn('string', 'redirect', ['length' => 255, 'comment' => '跳转地址'])->nullable(); + $table->addColumn('smallInteger', 'is_hidden', ['default' => 1, 'comment' => '是否隐藏 (1是 2否)']); + $table->addColumn('char', 'type', ['length' => 1, 'default' => '', 'comment' => '菜单类型, (M菜单 B按钮 L链接 I iframe)']); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('smallInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_menu'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_215515_create_system_role_table.php b/app/System/Database/Migrations/2021_04_18_215515_create_system_role_table.php new file mode 100644 index 0000000..3320fac --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_215515_create_system_role_table.php @@ -0,0 +1,54 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemRoleTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_role', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('角色信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 30, 'comment' => '角色名称']); + $table->addColumn('string', 'code', ['length' => 100, 'comment' => '角色代码']); + $table->addColumn( + 'smallInteger', 'data_scope', + [ + 'length' => 1, + 'default' => 1, + 'comment' => '数据范围(1:全部数据权限 2:自定义数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:本人数据权限)' + ])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('smallInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_role'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_215521_create_system_user_role_table.php b/app/System/Database/Migrations/2021_04_18_215521_create_system_user_role_table.php new file mode 100644 index 0000000..4355006 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_215521_create_system_user_role_table.php @@ -0,0 +1,39 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemUserRoleTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_user_role', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('用户与角色关联表'); + $table->addColumn('bigInteger', 'user_id', ['unsigned' => true, 'comment' => '用户主键']); + $table->addColumn('bigInteger', 'role_id', ['unsigned' => true, 'comment' => '角色主键']); + $table->primary(['user_id', 'role_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_user_role'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_222634_create_system_role_menu_table.php b/app/System/Database/Migrations/2021_04_18_222634_create_system_role_menu_table.php new file mode 100644 index 0000000..6ed044d --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_222634_create_system_role_menu_table.php @@ -0,0 +1,39 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemRoleMenuTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_role_menu', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('角色与菜单关联表'); + $table->addColumn('bigInteger', 'role_id', ['unsigned' => true, 'comment' => '角色主键']); + $table->addColumn('bigInteger', 'menu_id', ['unsigned' => true, 'comment' => '菜单主键']); + $table->primary(['role_id', 'menu_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_role_menu'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224626_create_setting_config_table.php b/app/System/Database/Migrations/2021_04_18_224626_create_setting_config_table.php new file mode 100644 index 0000000..2cc50c4 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224626_create_setting_config_table.php @@ -0,0 +1,37 @@ +engine = 'Innodb'; + $table->comment('参数配置信息表'); + $table->addColumn('bigInteger', 'group_id', ['comment' => '组id']); + $table->addColumn('string', 'key', ['length'=> 32, 'comment' => '配置键名'])->nullable(); + $table->addColumn('string', 'value', ['length'=> 255, 'comment' => '配置值'])->nullable(); + $table->addColumn('string', 'name', ['length'=> 255, 'comment' => '配置名称'])->nullable(); + $table->addColumn('string', 'input_type', ['length'=> 32, 'comment' => '数据输入类型'])->nullable(); + $table->addColumn('string', 'config_select_data', ['length'=> 500, 'comment' => '配置选项数据'])->nullable(); + $table->addColumn('smallInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->primary('key'); + $table->index('group_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_config'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224723_create_system_dict_data_table.php b/app/System/Database/Migrations/2021_04_18_224723_create_system_dict_data_table.php new file mode 100644 index 0000000..310afff --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224723_create_system_dict_data_table.php @@ -0,0 +1,50 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemDictDataTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_dict_data', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('字典数据表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'type_id', ['unsigned' => true, 'comment' => '字典类型ID']); + $table->addColumn('string', 'label', ['length' => 50, 'comment' => '字典标签'])->nullable(); + $table->addColumn('string', 'value', ['length' => 100, 'comment' => '字典值'])->nullable(); + $table->addColumn('string', 'code', ['length' => 100, 'comment' => '字典标示'])->nullable(); + $table->addColumn('smallInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('type_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_dict_data'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224727_create_system_dict_type_table.php b/app/System/Database/Migrations/2021_04_18_224727_create_system_dict_type_table.php new file mode 100644 index 0000000..c12b061 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224727_create_system_dict_type_table.php @@ -0,0 +1,46 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemDictTypeTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_dict_type', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('字典类型表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 50, 'comment' => '字典名称'])->nullable(); + $table->addColumn('string', 'code', ['length' => 100, 'comment' => '字典标示'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_dict_type'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224817_create_system_dept_table.php b/app/System/Database/Migrations/2021_04_18_224817_create_system_dept_table.php new file mode 100644 index 0000000..64dff1d --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224817_create_system_dept_table.php @@ -0,0 +1,51 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemDeptTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_dept', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('部门信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'parent_id', ['unsigned' => true, 'comment' => '父ID']); + $table->addColumn('string', 'level', ['length' => 500, 'comment' => '组级集合']); + $table->addColumn('string', 'name', ['length' => 30, 'comment' => '部门名称']); + $table->addColumn('string', 'leader', ['length' => 20, 'comment' => '负责人'])->nullable(); + $table->addColumn('string', 'phone', ['length' => 11, 'comment' => '联系电话'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('smallInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('parent_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_dept'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224835_create_system_post_table.php b/app/System/Database/Migrations/2021_04_18_224835_create_system_post_table.php new file mode 100644 index 0000000..6da590a --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224835_create_system_post_table.php @@ -0,0 +1,47 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemPostTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_post', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('岗位信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 50, 'comment' => '岗位名称']); + $table->addColumn('string', 'code', ['length' => 100, 'comment' => '岗位代码']); + $table->addColumn('smallInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_job'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224912_create_system_login_log_table.php b/app/System/Database/Migrations/2021_04_18_224912_create_system_login_log_table.php new file mode 100644 index 0000000..f13c369 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224912_create_system_login_log_table.php @@ -0,0 +1,47 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemLoginLogTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_login_log', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('登录日志表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'username', ['length' => 20, 'comment' => '用户名']); + $table->addColumn('ipAddress', 'ip', ['comment' => '登录IP地址'])->nullable(); + $table->addColumn('string', 'ip_location', ['length' => 255, 'comment' => 'IP所属地'])->nullable(); + $table->addColumn('string', 'os', ['length' => 50, 'comment' => '操作系统'])->nullable(); + $table->addColumn('string', 'browser', ['length' => 50, 'comment' => '浏览器'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '登录状态 (1成功 2失败)']); + $table->addColumn('string', 'message', ['length' => 50, 'comment' => '提示消息'])->nullable(); + $table->addColumn('timestamp', 'login_time', ['comment' => '登录时间']); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('username'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_login_log'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_224938_create_system_oper_log_table.php b/app/System/Database/Migrations/2021_04_18_224938_create_system_oper_log_table.php new file mode 100644 index 0000000..a16ecbf --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_224938_create_system_oper_log_table.php @@ -0,0 +1,44 @@ +engine = 'Innodb'; + $table->comment('操作日志表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'username', ['length' => 20, 'comment' => '用户名']); + $table->addColumn('string', 'method', ['length' => 20, 'comment' => '请求方式']); + $table->addColumn('string', 'router', ['length' => 500, 'comment' => '请求路由']); + $table->addColumn('string', 'service_name', ['length' => 30, 'comment' => '业务名称']); + $table->addColumn('ipAddress', 'ip', ['comment' => '请求IP地址'])->nullable(); + $table->addColumn('string', 'ip_location', ['length' => 255, 'comment' => 'IP所属地'])->nullable(); + $table->addColumn('text', 'request_data', ['comment' => '请求数据'])->nullable(); + $table->addColumn('string', 'response_code', ['length' => 5, 'comment' => '响应状态码'])->nullable(); + $table->addColumn('text', 'response_data', ['comment' => '响应数据'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('username'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_oper_log'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_225055_create_setting_crontab_table.php b/app/System/Database/Migrations/2021_04_18_225055_create_setting_crontab_table.php new file mode 100644 index 0000000..bd7cb63 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_225055_create_setting_crontab_table.php @@ -0,0 +1,46 @@ +engine = 'Innodb'; + $table->comment('定时任务信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 100, 'comment' => '任务名称'])->nullable(); + $table->addColumn( + 'smallInteger', 'type', + ['default' => 4, 'comment' => '任务类型 (1 command, 2 class, 3 url, 4 eval)'] + )->nullable(); + $table->addColumn('string', 'target', ['length' => 500, 'comment' => '调用任务字符串'])->nullable(); + $table->addColumn('string', 'parameter', ['length' => 1000, 'comment' => '调用任务参数'])->nullable(); + $table->addColumn('string', 'rule', ['length' => 32, 'comment' => '任务执行表达式'])->nullable(); + $table->addColumn( + 'smallInteger', 'singleton', + ['default' => 1, 'comment' => '是否单次执行 (1 是 2 不是)'] + )->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_task'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_225100_create_setting_crontab_log_table.php b/app/System/Database/Migrations/2021_04_18_225100_create_setting_crontab_log_table.php new file mode 100644 index 0000000..1f7d272 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_225100_create_setting_crontab_log_table.php @@ -0,0 +1,35 @@ +engine = 'Innodb'; + $table->comment('定时任务执行日志表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'crontab_id', ['unsigned' => true, 'comment' => '任务ID']); + $table->addColumn('string', 'name', ['length'=> 255, 'comment' => '任务名称'])->nullable(); + $table->addColumn('string', 'target', ['length'=> 500, 'comment' => '任务调用目标字符串'])->nullable(); + $table->addColumn('string', 'parameter', ['length'=> 1000, 'comment' => '任务调用参数'])->nullable(); + $table->addColumn('string', 'exception_info', ['length'=> 2000, 'comment' => '异常信息'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '执行状态 (1成功 2失败)'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('setting_crontab_log'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_225100_create_setting_generate_columns_table.php b/app/System/Database/Migrations/2021_04_18_225100_create_setting_generate_columns_table.php new file mode 100644 index 0000000..d764aa5 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_225100_create_setting_generate_columns_table.php @@ -0,0 +1,63 @@ +engine = 'Innodb'; + $table->comment('代码生成业务字段信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'table_id', ['unsigned' => true, 'comment' => '所属表ID']); + $table->addColumn('string', 'column_name', ['length' => 200, 'comment' => '字段名称'])->nullable(); + $table->addColumn('string', 'column_comment', ['length' => 255, 'comment' => '字段注释'])->nullable(); + $table->addColumn('string', 'column_type', ['length' => 50, 'comment' => '字段类型'])->nullable(); + $table->addColumn('smallInteger', 'is_pk', ['default' => 1, 'comment' => '1 非主键 2 主键'])->nullable(); + $table->addColumn('smallInteger', 'is_required', ['default' => 1, 'comment' => '1 非必填 2 必填'])->nullable(); + $table->addColumn('smallInteger', 'is_insert', ['default' => 1, 'comment' => '1 非插入字段 2 插入字段'])->nullable(); + $table->addColumn('smallInteger', 'is_edit', ['default' => 1, 'comment' => '1 非编辑字段 2 编辑字段'])->nullable(); + $table->addColumn('smallInteger', 'is_list', ['default' => 1, 'comment' => '1 非列表显示字段 2 列表显示字段'])->nullable(); + $table->addColumn('smallInteger', 'is_query', ['default' => 1, 'comment' => '1 非查询字段 2 查询字段'])->nullable(); + $table->addColumn( + 'string', 'query_type', + [ + 'length' => 100, + 'default' => 'eq', + 'comment' => '查询方式 eq 等于, neq 不等于, gt 大于, lt 小于, like 范围' + ] + )->nullable(); + $table->addColumn( + 'string', 'view_type', + [ + 'length' => 100, + 'default' => 'text', + 'comment' => '页面控件,text, textarea, password, select, checkbox, radio, date, upload, ma-upload(封装的上传控件)' + ] + )->nullable(); + $table->addColumn('string', 'dict_type', ['length' => 200, 'comment' => '字典类型'])->nullable(); + $table->addColumn('string', 'allow_roles', ['length' => 255, 'comment' => '允许查看该字段的角色'])->nullable(); + $table->addColumn('string', 'options', ['length' => 1000, 'comment' => '字段其他设置'])->nullable(); + $table->addColumn('tinyInteger', 'sort', ['unsigned' => true, 'default' => 0, 'comment' => '排序'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('setting_generate_columns'); + } +} diff --git a/app/System/Database/Migrations/2021_04_18_225100_create_setting_generate_tables_table.php b/app/System/Database/Migrations/2021_04_18_225100_create_setting_generate_tables_table.php new file mode 100644 index 0000000..fed6af5 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_18_225100_create_setting_generate_tables_table.php @@ -0,0 +1,49 @@ +engine = 'Innodb'; + $table->comment('代码生成业务信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'table_name', ['length' => 200, 'comment' => '表名称'])->nullable(); + $table->addColumn('string', 'table_comment', ['length' => 500, 'comment' => '表注释'])->nullable(); + $table->addColumn('string', 'module_name', ['length' => 100, 'comment' => '所属模块'])->nullable(); + $table->addColumn('string', 'namespace', ['length' => 255, 'comment' => '命名空间'])->nullable(); + $table->addColumn('string', 'menu_name', ['length' => 100, 'comment' => '生成菜单名'])->nullable(); + $table->addColumn('bigInteger', 'belong_menu_id', ['length' => 100, 'comment' => '所属菜单'])->nullable(); + $table->addColumn('string', 'package_name', ['length' => 100, 'comment' => '控制器包名'])->nullable(); + $table->addColumn( + 'string', 'type', + ['length' => 100, 'comment' => '生成类型,single 单表CRUD,tree 树表CRUD,parent_sub父子表CRUD']) + ->nullable(); + $table->addColumn('smallInteger', 'generate_type', ['default' => 1, 'comment' => '1 压缩包下载 2 生成到模块'])->nullable(); + $table->addColumn('string', 'generate_menus', ['length' => 255, 'comment' => '生成菜单列表'])->nullable(); + $table->addColumn('smallInteger', 'build_menu', ['default' => 1, 'comment' => '是否构建菜单'])->nullable(); + $table->addColumn('smallInteger', 'component_type', ['default' => 1, 'comment' => '组件显示方式'])->nullable(); + $table->addColumn('string', 'options', ['length' => 1500, 'comment' => '其他业务选项'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('setting_generate_tables'); + } +} diff --git a/app/System/Database/Migrations/2021_04_26_141249_create_system_user_post_table.php b/app/System/Database/Migrations/2021_04_26_141249_create_system_user_post_table.php new file mode 100644 index 0000000..f95bda7 --- /dev/null +++ b/app/System/Database/Migrations/2021_04_26_141249_create_system_user_post_table.php @@ -0,0 +1,39 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemUserPostTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_user_post', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('用户与岗位关联表'); + $table->addColumn('bigInteger', 'user_id', ['unsigned' => true, 'comment' => '用户主键']); + $table->addColumn('bigInteger', 'post_id', ['unsigned' => true, 'comment' => '岗位主键']); + $table->primary(['user_id', 'post_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_user_job'); + } +} diff --git a/app/System/Database/Migrations/2021_05_07_215228_create_system_role_dept_table.php b/app/System/Database/Migrations/2021_05_07_215228_create_system_role_dept_table.php new file mode 100644 index 0000000..a604fce --- /dev/null +++ b/app/System/Database/Migrations/2021_05_07_215228_create_system_role_dept_table.php @@ -0,0 +1,39 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemRoleDeptTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_role_dept', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('角色与部门关联表'); + $table->addColumn('bigInteger', 'role_id', ['unsigned' => true, 'comment' => '角色主键']); + $table->addColumn('bigInteger', 'dept_id', ['unsigned' => true, 'comment' => '部门主键']); + $table->primary(['role_id', 'dept_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_role_dept'); + } +} diff --git a/app/System/Database/Migrations/2021_06_24_111216_create_system_uploadfile_table.php b/app/System/Database/Migrations/2021_06_24_111216_create_system_uploadfile_table.php new file mode 100644 index 0000000..bb6e2c8 --- /dev/null +++ b/app/System/Database/Migrations/2021_06_24_111216_create_system_uploadfile_table.php @@ -0,0 +1,55 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemUploadfileTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_uploadfile', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('上传文件信息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('smallInteger', 'storage_mode', ['default' => 1, 'comment' => '存储模式 (1 本地 2 阿里云 3 七牛云 4 腾讯云)'])->nullable(); + $table->addColumn('string', 'origin_name', ['length' => 255, 'comment' => '原文件名'])->nullable(); + $table->addColumn('string', 'object_name', ['length' => 50, 'comment' => '新文件名'])->nullable(); + $table->addColumn('string', 'hash', ['length' => 64, 'comment' => '文件hash'])->nullable(); + $table->addColumn('string', 'mime_type', ['length' => 255, 'comment' => '资源类型'])->nullable(); + $table->addColumn('string', 'storage_path', ['length' => 100, 'comment' => '存储目录'])->nullable(); + $table->addColumn('string', 'suffix', ['length' => 10, 'comment' => '文件后缀'])->nullable(); + $table->addColumn('bigInteger', 'size_byte', ['length' => 20, 'comment' => '字节数'])->nullable(); + $table->addColumn('string', 'size_info', ['length' => 50, 'comment' => '文件大小'])->nullable(); + $table->addColumn('string', 'url', ['length' => 255, 'comment' => 'url地址'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('storage_path'); + $table->unique('hash'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_uploadfile'); + } +} diff --git a/app/System/Database/Migrations/2021_11_11_140915_create_system_app_table.php b/app/System/Database/Migrations/2021_11_11_140915_create_system_app_table.php new file mode 100644 index 0000000..b9bd610 --- /dev/null +++ b/app/System/Database/Migrations/2021_11_11_140915_create_system_app_table.php @@ -0,0 +1,50 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemAppTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_app', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('应用表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'group_id', ['unsigned' => true, 'comment' => '应用组ID']); + $table->addColumn('string', 'app_name', ['length' => 32, 'comment' => '应用名称']); + $table->addColumn('string', 'app_id', ['length' => 16, 'comment' => '应用ID']); + $table->addColumn('string', 'app_secret', ['length' => 128, 'comment' => '应用密钥']); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('text', 'description', ['comment' => '应用介绍'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index(['group_id', 'app_id', 'app_secret']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_app'); + } +} diff --git a/app/System/Database/Migrations/2021_11_11_140935_create_system_app_group_table.php b/app/System/Database/Migrations/2021_11_11_140935_create_system_app_group_table.php new file mode 100644 index 0000000..5102015 --- /dev/null +++ b/app/System/Database/Migrations/2021_11_11_140935_create_system_app_group_table.php @@ -0,0 +1,45 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemAppGroupTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_app_group', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('应用分组表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 32, 'comment' => '应用组名称']); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_app_group'); + } +} diff --git a/app/System/Database/Migrations/2021_11_11_141720_create_system_api_table.php b/app/System/Database/Migrations/2021_11_11_141720_create_system_api_table.php new file mode 100644 index 0000000..73556a8 --- /dev/null +++ b/app/System/Database/Migrations/2021_11_11_141720_create_system_api_table.php @@ -0,0 +1,55 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemApiTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_api', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('接口表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'group_id', ['unsigned' => true, 'comment' => '接口组ID']); + $table->addColumn('string', 'name', ['length' => 32, 'comment' => '接口名称']); + $table->addColumn('string', 'access_name', ['length' => 64, 'comment' => '接口访问名称']); + $table->addColumn('string', 'class_name', ['length' => 128, 'comment' => '类命名空间']); + $table->addColumn('string', 'method_name', ['length' => 128, 'comment' => '方法名']); + $table->addColumn('smallInteger', 'auth_mode', ['default' => 1, 'comment' => '认证模式 (1简易 2复杂)']); + $table->addColumn('char', 'request_mode', ['length' => 1, 'default' => 'A', 'comment' => '请求模式 (A 所有 P POST G GET)']); + $table->addColumn('text', 'description', ['comment' => '接口说明介绍'])->nullable(); + $table->addColumn('text', 'response', ['comment' => '返回内容示例'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('group_id'); + $table->index('access_name'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_api'); + } +} diff --git a/app/System/Database/Migrations/2021_11_11_141724_create_system_api_group_table.php b/app/System/Database/Migrations/2021_11_11_141724_create_system_api_group_table.php new file mode 100644 index 0000000..c8bb2df --- /dev/null +++ b/app/System/Database/Migrations/2021_11_11_141724_create_system_api_group_table.php @@ -0,0 +1,45 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemApiGroupTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_api_group', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('接口分组表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 32, 'comment' => '接口组名称']); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_api_group'); + } +} diff --git a/app/System/Database/Migrations/2021_11_11_141849_create_system_api_column_table.php b/app/System/Database/Migrations/2021_11_11_141849_create_system_api_column_table.php new file mode 100644 index 0000000..1509f5f --- /dev/null +++ b/app/System/Database/Migrations/2021_11_11_141849_create_system_api_column_table.php @@ -0,0 +1,52 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemApiColumnTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_api_column', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('接口字段表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'api_id', ['unsigned' => true, 'comment' => '接口主键']); + $table->addColumn('string', 'name', ['length' => 64, 'comment' => '字段名称']); + $table->addColumn('smallInteger', 'type', ['default' => 1, 'comment' => '字段类型 1 请求 2 返回']); + $table->addColumn('string', 'data_type', ['length' => 16, 'comment' => '数据类型']); + $table->addColumn('smallInteger', 'is_required', ['default' => 1, 'comment' => '是否必填 1 非必填 2 必填']); + $table->addColumn('string', 'default_value', ['length' => 500, 'comment' => '默认值'])->nullable(); + $table->addColumn('smallInteger', 'status', ['default' => 1, 'comment' => '状态 (1正常 2停用)'])->nullable(); + $table->addColumn('string', 'description', ['length' => 500, 'comment' => '字段说明'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index(['api_id', 'type', 'status']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_api_column'); + } +} diff --git a/app/System/Database/Migrations/2021_11_11_151525_create_system_app_api_table.php b/app/System/Database/Migrations/2021_11_11_151525_create_system_app_api_table.php new file mode 100644 index 0000000..0130126 --- /dev/null +++ b/app/System/Database/Migrations/2021_11_11_151525_create_system_app_api_table.php @@ -0,0 +1,39 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemAppApiTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_app_api', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('应用和api关联表'); + $table->addColumn('bigInteger', 'app_id', ['unsigned' => true, 'comment' => '应用ID']); + $table->addColumn('bigInteger', 'api_id', ['unsigned' => true, 'comment' => 'API—ID']); + $table->primary(['app_id', 'api_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_app_api'); + } +} diff --git a/app/System/Database/Migrations/2021_11_26_163202_create_system_api_log_table.php b/app/System/Database/Migrations/2021_11_26_163202_create_system_api_log_table.php new file mode 100644 index 0000000..b7a030a --- /dev/null +++ b/app/System/Database/Migrations/2021_11_26_163202_create_system_api_log_table.php @@ -0,0 +1,48 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemApiLogTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_api_log', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('接口日志表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'api_id', ['unsigned' => true, 'comment' => 'api ID']); + $table->addColumn('string', 'api_name', ['length' => 32, 'comment' => '接口名称']); + $table->addColumn('string', 'access_name', ['length' => 64, 'comment' => '接口访问名称']); + $table->addColumn('text', 'request_data', ['comment' => '请求数据'])->nullable(); + $table->addColumn('string', 'response_code', ['length' => 5, 'comment' => '响应状态码'])->nullable(); + $table->addColumn('text', 'response_data', ['comment' => '响应数据'])->nullable(); + $table->addColumn('ipAddress', 'ip', ['comment' => '访问IP地址'])->nullable(); + $table->addColumn('string', 'ip_location', ['length' => 255, 'comment' => 'IP所属地'])->nullable(); + $table->addColumn('timestamp', 'access_time', ['precision' => 0, 'comment' => '访问时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index(['api_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_api_log'); + } +} diff --git a/app/System/Database/Migrations/2021_11_26_163818_create_system_notice_table.php b/app/System/Database/Migrations/2021_11_26_163818_create_system_notice_table.php new file mode 100644 index 0000000..d9221bd --- /dev/null +++ b/app/System/Database/Migrations/2021_11_26_163818_create_system_notice_table.php @@ -0,0 +1,49 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemNoticeTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_notice', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('系统公告表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'message_id')->comment('消息ID'); + $table->addColumn('string', 'title', ['length' => 255, 'comment' => '标题']); + $table->addColumn('smallInteger', 'type', ['comment' => '公告类型(1通知 2公告)']); + $table->addColumn('text', 'content', ['length' => 1, 'comment' => '公告内容'])->nullable(); + $table->addColumn('integer', 'click_num', ['comment' => '浏览次数', 'default' => 0])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index('message_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_notice'); + } +} diff --git a/app/System/Database/Migrations/2021_11_26_164006_create_system_queue_message_table.php b/app/System/Database/Migrations/2021_11_26_164006_create_system_queue_message_table.php new file mode 100644 index 0000000..23389e3 --- /dev/null +++ b/app/System/Database/Migrations/2021_11_26_164006_create_system_queue_message_table.php @@ -0,0 +1,48 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemQueueMessageTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_queue_message', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('队列消息表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'content_id', ['unsigned' => true, 'comment' => '内容ID'])->nullable(); + $table->addColumn('string', 'content_type', ['length' => 64, 'comment' => '内容类型'])->nullable(); + $table->addColumn('string', 'title', ['length' => 255, 'comment' => '消息标题'])->nullable(); + $table->addColumn('bigInteger', 'send_by', ['unsigned' => true, 'comment' => '发送人'])->nullable(); + $table->addColumn('longtext', 'content', ['comment' => '消息内容'])->nullable(); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + $table->index(['content_type']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_queue_message'); + } +} diff --git a/app/System/Database/Migrations/2021_12_25_163880_create_system_queue_log_table.php b/app/System/Database/Migrations/2021_12_25_163880_create_system_queue_log_table.php new file mode 100644 index 0000000..c295f2e --- /dev/null +++ b/app/System/Database/Migrations/2021_12_25_163880_create_system_queue_log_table.php @@ -0,0 +1,48 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemQueueLogTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_queue_log', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('队列日志表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'exchange_name', ['length' => 32, 'comment' => '交换机名称']); + $table->addColumn('string', 'routing_key_name', ['length' => 32, 'comment' => '路由名称']); + $table->addColumn('string', 'queue_name', ['length' => 64, 'comment' => '队列名称']); + $table->addColumn('longtext', 'queue_content', ['comment' => '队列数据'])->nullable(); + $table->addColumn('text', 'log_content', ['comment' => '队列日志'])->nullable(); + $table->addColumn('smallInteger', 'produce_status', ['default' => 1, 'comment' => '生产状态 1:未生产 2:生产中 3:生产成功 4:生产失败 5:生产重复'])->nullable(); + $table->addColumn('smallInteger', 'consume_status', ['default' => 1, 'comment' => '消费状态 1:未消费 2:消费中 3:消费成功 4:消费失败 5:消费重复'])->nullable(); + $table->addColumn('integer', 'delay_time', ['unsigned' => true, 'comment' => '延迟时间(秒)']); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_queue_log'); + } +} diff --git a/app/System/Database/Migrations/2021_12_25_163890_create_system_queue_message_receive_table.php b/app/System/Database/Migrations/2021_12_25_163890_create_system_queue_message_receive_table.php new file mode 100644 index 0000000..c18ff97 --- /dev/null +++ b/app/System/Database/Migrations/2021_12_25_163890_create_system_queue_message_receive_table.php @@ -0,0 +1,40 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class CreateSystemQueueMessageReceiveTable extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('system_queue_message_receive', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('队列消息接收人表'); + $table->addColumn('bigInteger', 'message_id', ['unsigned' => true, 'comment' => '队列消息主键']); + $table->addColumn('bigInteger', 'user_id', ['unsigned' => true, 'comment' => '接收用户主键']); + $table->addColumn('smallInteger', 'read_status', ['default' => 1, 'comment' => '已读状态 (1未读 2已读)'])->nullable(); + $table->primary(['message_id', 'user_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_queue_message_receive'); + } +} diff --git a/app/System/Database/Migrations/2022_07_24_225626_create_setting_config_group_table.php b/app/System/Database/Migrations/2022_07_24_225626_create_setting_config_group_table.php new file mode 100644 index 0000000..e248823 --- /dev/null +++ b/app/System/Database/Migrations/2022_07_24_225626_create_setting_config_group_table.php @@ -0,0 +1,35 @@ +engine = 'Innodb'; + $table->comment('参数配置分组表'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('string', 'name', ['length' => 32, 'comment' => '配置组名称']); + $table->addColumn('string', 'code', ['length' => 64, 'comment' => '配置组标识']); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('system_config_group'); + } +} diff --git a/app/System/Database/Migrations/Update/2022_10_14_231534_update_version101.php b/app/System/Database/Migrations/Update/2022_10_14_231534_update_version101.php new file mode 100644 index 0000000..68709ab --- /dev/null +++ b/app/System/Database/Migrations/Update/2022_10_14_231534_update_version101.php @@ -0,0 +1,34 @@ +addColumn('smallInteger','is_sort') + ->comment('1 不排序 2 排序字段') + ->default(1) + ->after('is_query') + ->nullable(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('setting_generate_columns', function (Blueprint $table) { + $table->dropColumn(['is_sort']); + }); + } +} diff --git a/app/System/Database/Migrations/Update/2022_12_21_231534_update_version102.php b/app/System/Database/Migrations/Update/2022_12_21_231534_update_version102.php new file mode 100644 index 0000000..45ebd7c --- /dev/null +++ b/app/System/Database/Migrations/Update/2022_12_21_231534_update_version102.php @@ -0,0 +1,57 @@ +dropColumn(['dept_id']); + } + }); + + // 新增用户部门中间表 + Schema::create('system_user_dept', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('用户与部门关联表'); + $table->addColumn('bigInteger', 'user_id', ['unsigned' => true, 'comment' => '用户主键']); + $table->addColumn('bigInteger', 'dept_id', ['unsigned' => true, 'comment' => '部门主键']); + $table->primary(['user_id', 'dept_id']); + }); + + // 新增部门领导表 + Schema::create('system_dept_leader', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('部门领导表'); + $table->addColumn('bigInteger', 'dept_id', ['unsigned' => true, 'comment' => '部门主键']); + $table->addColumn('bigInteger', 'user_id', ['unsigned' => true, 'comment' => '用户主键']); + $table->addColumn('string', 'username', ['length' => 20, 'comment' => '用户名']); + $table->timestamp('created_at')->comment('添加时间'); + $table->primary(['dept_id', 'user_id']); + }); + + // 设置超管默认部门 + Db::table('system_user_dept')->insert([ 'user_id' => env('SUPER_ADMIN', 1), 'dept_id' => 1 ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('system_user', function (Blueprint $table) { + if (Schema::hasColumn('system_user','dept_id')) { + $table->addColumn('bigInteger', 'dept_id', ['unsigned' => true, 'comment' => '部门ID'])->nullable(); + } + }); + } +} diff --git a/app/System/Database/Seeders/setting_config_group_seeder.php b/app/System/Database/Seeders/setting_config_group_seeder.php new file mode 100644 index 0000000..d73bad4 --- /dev/null +++ b/app/System/Database/Seeders/setting_config_group_seeder.php @@ -0,0 +1,37 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SettingConfigGroupSeeder extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run() + { + Db::table('setting_config_group')->truncate(); + $tableName = env('DB_PREFIX') . \App\Setting\Model\SettingConfigGroup::getModel()->getTable(); + $sql = [ + "INSERT INTO `{$tableName}`(`id`, `name`, `code`, `created_by`, `updated_by`, `created_at`, `updated_at`, `remark`) VALUES (1, '站点配置', 'site_config', 1, 1, '2022-07-23 15:08:44', '2022-07-23 15:08:44', NULL)", + "INSERT INTO `{$tableName}`(`id`, `name`, `code`, `created_by`, `updated_by`, `created_at`, `updated_at`, `remark`) VALUES (2, '上传配置', 'upload_config', 1, 1, '2022-07-23 15:09:31', '2022-07-23 15:09:33', NULL)", + + ]; + foreach ($sql as $item) { + Db::insert($item); + } + } +} diff --git a/app/System/Database/Seeders/setting_config_seeder.php b/app/System/Database/Seeders/setting_config_seeder.php new file mode 100644 index 0000000..bc91470 --- /dev/null +++ b/app/System/Database/Seeders/setting_config_seeder.php @@ -0,0 +1,42 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SettingConfigSeeder extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run() + { + Db::table('setting_config')->truncate(); + $tableName = env('DB_PREFIX') . \App\Setting\Model\SettingConfig::getModel()->getTable(); + $sql = [ + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (1, 'site_copyright', NULL, '版权信息', 'textarea', NULL, 96, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (1, 'site_desc', NULL, '网站描述', 'textarea', NULL, 97, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (1, 'site_keywords', '后台管理系统', '网站关键字', 'input', NULL, 98, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (1, 'site_name', 'MineAdmin', '网站名称', 'input', NULL, 99, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (1, 'site_record_number', NULL, '网站备案号', 'input', NULL, 95, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_allow_file', 'txt,doc,docx,xls,xlsx,ppt,pptx,rar,zip,7z,gz,pdf,wps,md', '文件类型', 'input', NULL, 0, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_allow_image', 'jpg,jpeg,png,gif,svg,bmp', '图片类型', 'input', NULL, 0, NULL)", + "INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_mode', '1', '上传模式', 'select', '[{\"label\":\"本地上传\",\"value\":\"1\"},{\"label\":\"阿里云OSS\",\"value\":\"2\"},{\"label\":\"七牛云\",\"value\":\"3\"},{\"label\":\"腾讯云COS\",\"value\":\"4\"}]', 99, NULL)", + ]; + foreach ($sql as $item) { + Db::insert($item); + } + } +} diff --git a/app/System/Database/Seeders/setting_crontab.php b/app/System/Database/Seeders/setting_crontab.php new file mode 100644 index 0000000..89f29c9 --- /dev/null +++ b/app/System/Database/Seeders/setting_crontab.php @@ -0,0 +1,40 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SettingCrontab extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run() + { + Db::table('setting_crontab')->truncate(); + foreach ($this->getData() as $item) { + Db::insert($item); + } + } + + public function getData(): array + { + $tableName = env('DB_PREFIX').\App\Setting\Model\SettingCrontab::getModel()->getTable(); + return [ + "INSERT INTO `{$tableName}` VALUES (1, 'urlCrontab', '3', 'http://127.0.0.1:9501/', '', '59 */1 * * * *', 2, 2, NULL, NULL, '2021-08-07 23:28:28', '2021-08-07 23:44:55', '请求127.0.0.1')", + "INSERT INTO `{$tableName}` VALUES (2, '每月1号清理所有日志', '2', 'App\System\Crontab\ClearLogCrontab', '', '0 0 0 1 * *', 2, 2, NULL, NULL, '2022-04-11 11:15:48', '2022-04-11 11:15:48', '')" + ]; + } +} diff --git a/app/System/Database/Seeders/system_dept_seeder.php b/app/System/Database/Seeders/system_dept_seeder.php new file mode 100644 index 0000000..b1ae7e1 --- /dev/null +++ b/app/System/Database/Seeders/system_dept_seeder.php @@ -0,0 +1,42 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SystemDeptSeeder extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function run() + { + Db::table('system_dept')->truncate(); + Db::table('system_dept')->insert( + [ + 'parent_id' => 0, + 'level' => '0', + 'name' => '曼艺科技', + 'leader' => '曼艺', + 'phone' => '16888888888', + 'created_by' => env('SUPER_ADMIN', 1), + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ] + ); + } +} diff --git a/app/System/Database/Seeders/system_dict_data_seeder.php b/app/System/Database/Seeders/system_dict_data_seeder.php new file mode 100644 index 0000000..a5f979d --- /dev/null +++ b/app/System/Database/Seeders/system_dict_data_seeder.php @@ -0,0 +1,87 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SystemDictDataSeeder extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run() + { + Db::table('system_dict_data')->truncate(); + + foreach ($this->getData() as $item) { + Db::insert($item); + } + } + + protected function getData(): array + { + $tableName = env('DB_PREFIX') . \App\System\Model\SystemDictData::getModel()->getTable(); + return [ + "INSERT INTO `{$tableName}` VALUES (1, 1, 'InnoDB', 'InnoDB', 'table_engine', 0, 1, NULL, NULL, '2021-06-27 00:37:11', '2021-06-27 13:33:29', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (2, 1, 'MyISAM', 'MyISAM', 'table_engine', 0, 1, NULL, NULL, '2021-06-27 00:37:21', '2021-06-27 13:33:29', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (3, 2, '本地存储', '1', 'upload_mode', 99, 1, NULL, NULL, '2021-06-27 13:33:43', '2021-06-27 13:33:43', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (4, 2, '阿里云OSS', '2', 'upload_mode', 98, 1, NULL, NULL, '2021-06-27 13:33:55', '2021-06-27 13:33:55', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (5, 2, '七牛云', '3', 'upload_mode', 97, 1, NULL, NULL, '2021-06-27 13:34:07', '2021-06-27 13:34:07', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (6, 2, '腾讯云COS', '4', 'upload_mode', 96, 1, NULL, NULL, '2021-06-27 13:34:19', '2021-06-27 13:34:19', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (7, 3, '正常', '1', 'data_status', 0, 1, NULL, NULL, '2021-06-27 13:36:51', '2021-06-27 13:37:01', NULL, '0为正常')", + "INSERT INTO `{$tableName}` VALUES (8, 3, '停用', '2', 'data_status', 0, 1, NULL, NULL, '2021-06-27 13:37:10', '2021-06-27 13:37:10', NULL, '1为停用')", + "INSERT INTO `{$tableName}` VALUES (9, 4, '统计页面', 'statistics', 'dashboard', 0, 1, NULL, NULL, '2021-08-09 12:53:53', '2021-08-09 12:53:53', NULL, '管理员用')", + "INSERT INTO `{$tableName}` VALUES (10, 4, '工作台', 'work', 'dashboard', 0, 1, NULL, NULL, '2021-08-09 12:54:18', '2021-08-09 12:54:18', NULL, '员工使用')", + "INSERT INTO `{$tableName}` VALUES (11, 5, '男', '1', 'sex', 0, 1, NULL, NULL, '2021-08-09 12:55:00', '2021-08-09 12:55:00', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (12, 5, '女', '2', 'sex', 0, 1, NULL, NULL, '2021-08-09 12:55:08', '2021-08-09 12:55:08', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (13, 5, '未知', '3', 'sex', 0, 1, NULL, NULL, '2021-08-09 12:55:16', '2021-08-09 12:55:16', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (14, 6, 'String', '1', 'api_data_type', 7, 1, NULL, NULL, '2021-11-23 10:49:00', '2021-11-23 10:49:00', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (15, 6, 'Integer', '2', 'api_data_type', 6, 1, NULL, NULL, '2021-11-23 10:49:29', '2021-11-23 10:49:29', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (16, 6, 'Array', '3', 'api_data_type', 5, 1, NULL, NULL, '2021-11-23 10:49:38', '2021-11-23 10:49:38', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (17, 6, 'Float', '4', 'api_data_type', 4, 1, NULL, NULL, '2021-11-23 10:49:46', '2021-11-23 10:49:46', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (18, 6, 'Boolean', '5', 'api_data_type', 3, 1, NULL, NULL, '2021-11-23 10:49:54', '2021-11-23 10:49:54', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (19, 6, 'Enum', '6', 'api_data_type', 2, 1, NULL, NULL, '2021-11-23 10:50:17', '2021-11-23 10:50:17', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (20, 6, 'Object', '7', 'api_data_type', 1, 1, NULL, NULL, '2021-11-23 10:50:26', '2021-11-23 10:50:26', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (21, 6, 'File', '8', 'api_data_type', 0, 1, NULL, NULL, '2021-12-25 18:32:48', '2021-12-25 18:32:48', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (22, 7, '通知', '1', 'backend_notice_type', 2, 1, NULL, NULL, '2021-11-11 17:29:27', '2021-11-11 17:30:51', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (23, 7, '公告', '2', 'backend_notice_type', 1, 1, NULL, NULL, '2021-11-11 17:31:42', '2021-11-11 17:31:42', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (24, 8, '所有', 'A', 'request_mode', 5, 1, NULL, NULL, '2021-11-14 17:23:25', '2021-11-14 17:23:25', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (25, 8, 'GET', 'G', 'request_mode', 4, 1, NULL, NULL, '2021-11-14 17:23:45', '2021-11-14 17:23:45', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (26, 8, 'POST', 'P', 'request_mode', 3, 1, NULL, NULL, '2021-11-14 17:23:38', '2021-11-14 17:23:38', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (27, 8, 'PUT', 'U', 'request_mode', 2, 1, NULL, NULL, '2021-11-14 17:23:45', '2021-11-14 17:23:45', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (28, 8, 'DELETE', 'D', 'request_mode', 1, 1, NULL, NULL, '2021-11-14 17:23:45', '2021-11-14 17:23:45', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (29, 9, '未生产', '1', 'queue_produce_status', 5, 1, NULL, NULL, '2021-12-25 18:25:28', '2021-12-25 18:25:28', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (30, 9, '生产中', '2', 'queue_produce_status', 4, 1, NULL, NULL, '2021-12-25 18:25:38', '2021-12-25 18:25:38', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (31, 9, '生产成功', '3', 'queue_produce_status', 3, 1, NULL, NULL, '2021-12-25 18:25:50', '2021-12-25 18:25:50', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (32, 9, '生产失败', '4', 'queue_produce_status', 2, 1, NULL, NULL, '2021-12-25 18:26:14', '2021-12-25 18:26:14', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (33, 9, '生产重复', '5', 'queue_produce_status', 1, 1, NULL, NULL, '2021-12-25 18:26:30', '2021-12-25 18:26:30', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (34, 10, '未消费', '1', 'queue_consume_status', 5, 1, NULL, NULL, '2021-12-25 18:26:57', '2021-12-25 18:26:57', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (35, 10, '消费中', '2', 'queue_consume_status', 4, 1, NULL, NULL, '2021-12-25 18:27:07', '2021-12-25 18:27:07', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (36, 10, '消费成功', '3', 'queue_consume_status', 3, 1, NULL, NULL, '2021-12-25 18:27:16', '2021-12-25 18:27:16', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (37, 10, '消费失败', '4', 'queue_consume_status', 2, 1, NULL, NULL, '2021-12-25 18:27:24', '2021-12-25 18:27:24', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (38, 10, '消费重复', '5', 'queue_consume_status', 1, 1, NULL, NULL, '2021-12-25 18:27:35', '2021-12-25 18:27:35', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (39, 11, '通知', 'notice', 'queue_msg_type', 1, 1, NULL, NULL, '2021-12-25 18:30:31', '2021-12-25 18:30:31', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (40, 11, '公告', 'announcement', 'queue_msg_type', 2, 1, NULL, NULL, '2021-12-25 18:31:00', '2021-12-25 18:31:00', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (41, 11, '待办', 'todo', 'queue_msg_type', 3, 1, NULL, NULL, '2021-12-25 18:31:26', '2021-12-25 18:31:26', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (42, 11, '抄送我的', 'carbon_copy_mine', 'queue_msg_type', 4, 1, NULL, NULL, '2021-12-25 18:31:26', '2021-12-25 18:31:26', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (43, 11, '私信', 'private_message', 'queue_msg_type', 5, 1, NULL, NULL, '2021-12-25 18:31:26', '2021-12-25 18:31:26', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (44, 12, '图片', 'image', 'attachment_type', 10, 1, NULL, NULL, '2022-03-17 14:49:59', '2022-03-17 14:49:59', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (45, 12, '文档', 'text', 'attachment_type', 9, 1, NULL, NULL, '2022-03-17 14:50:20', '2022-03-17 14:50:49', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (46, 12, '音频', 'audio', 'attachment_type', 8, 1, NULL, NULL, '2022-03-17 14:50:37', '2022-03-17 14:50:52', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (47, 12, '视频', 'video', 'attachment_type', 7, 1, NULL, NULL, '2022-03-17 14:50:45', '2022-03-17 14:50:57', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (48, 12, '应用程序', 'application', 'attachment_type', 6, 1, NULL, NULL, '2022-03-17 14:50:52', '2022-03-17 14:50:59', NULL, NULL)", + + ]; + } +} diff --git a/app/System/Database/Seeders/system_dict_type_seeder.php b/app/System/Database/Seeders/system_dict_type_seeder.php new file mode 100644 index 0000000..633c325 --- /dev/null +++ b/app/System/Database/Seeders/system_dict_type_seeder.php @@ -0,0 +1,52 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SystemDictTypeSeeder extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run() + { + Db::table('system_dict_type')->truncate(); + + foreach ($this->getData() as $item) { + Db::insert($item); + } + } + + protected function getData(): array + { + $tableName = env('DB_PREFIX') . \App\System\Model\SystemDictType::getModel()->getTable(); + return [ + "INSERT INTO `{$tableName}` VALUES (1, '数据表引擎', 'table_engine', 1, NULL, NULL, '2021-06-27 00:36:42', '2021-06-27 13:33:29', NULL, '数据表引擎字典')", + "INSERT INTO `{$tableName}` VALUES (2, '存储模式', 'upload_mode', 1, NULL, NULL, '2021-06-27 13:33:11', '2021-06-27 13:33:11', NULL, '上传文件存储模式')", + "INSERT INTO `{$tableName}` VALUES (3, '数据状态', 'data_status', 1, NULL, NULL, '2021-06-27 13:36:16', '2021-06-27 13:36:34', NULL, '通用数据状态')", + "INSERT INTO `{$tableName}` VALUES (4, '后台首页', 'dashboard', 1, NULL, NULL, '2021-08-09 12:53:17', '2021-08-09 12:53:17', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (5, '性别', 'sex', 1, NULL, NULL, '2021-08-09 12:54:40', '2021-08-09 12:54:40', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (6, '接口数据类型', 'api_data_type', 1, NULL, NULL, '2021-11-22 20:56:03', '2021-11-22 20:56:03', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (7, '后台公告类型', 'backend_notice_type', 1, NULL, NULL, '2021-11-11 17:29:05', '2021-11-11 17:29:14', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (8, '请求方式', 'request_mode', 1, NULL, NULL, '2021-11-14 17:22:52', '2021-11-14 17:22:52', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (9, '队列生产状态', 'queue_produce_status', 1, NULL, NULL, '2021-12-25 18:22:38', '2021-12-25 18:22:38', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (10, '队列消费状态', 'queue_consume_status', 1, NULL, NULL, '2021-12-25 18:23:19', '2021-12-25 18:23:19', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (11, '队列消息类型', 'queue_msg_type', 1, NULL, NULL, '2021-12-25 18:28:40', '2021-12-25 18:28:40', NULL, NULL)", + "INSERT INTO `{$tableName}` VALUES (12, '附件类型', 'attachment_type', 1, NULL, NULL, '2022-03-17 14:49:23', '2022-03-17 14:49:23', NULL, NULL)", + + ]; + } +} diff --git a/app/System/Database/Seeders/system_menu_seeder.php b/app/System/Database/Seeders/system_menu_seeder.php new file mode 100644 index 0000000..bb4e87d --- /dev/null +++ b/app/System/Database/Seeders/system_menu_seeder.php @@ -0,0 +1,235 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +use Hyperf\Database\Seeders\Seeder; +use Hyperf\DbConnection\Db; + +class SystemMenuSeeder extends Seeder +{ + /** + * Run the database seeds. + * + * @return void + */ + public function run() + { + Db::table('system_menu')->truncate(); + foreach ($this->getData() as $item) { + Db::insert($item); + } + } + + protected function getData(): array + { + $model = env('DB_PREFIX') . \App\System\Model\SystemMenu::getModel()->getTable(); + return [ + "INSERT INTO `{$model}` VALUES (1000, 0, '0', '权限', 'permission', 'ma-icon-permission', 'permission', '', NULL, '2', 'M', 1, 99, NULL, NULL, '2021-07-25 18:48:47', '2021-07-25 18:48:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1100, 1000, '0,1000', '用户管理', 'system:user', 'ma-icon-user', 'user', 'system/user/index', NULL, '2', 'M', 1, 99, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1101, 1100, '0,1000,1100', '用户列表', 'system:user:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1102, 1100, '0,1000,1100', '用户回收站列表', 'system:user:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1103, 1100, '0,1000,1100', '用户保存', 'system:user:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1104, 1100, '0,1000,1100', '用户更新', 'system:user:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1105, 1100, '0,1000,1100', '用户删除', 'system:user:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1106, 1100, '0,1000,1100', '用户读取', 'system:user:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1107, 1100, '0,1000,1100', '用户恢复', 'system:user:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1108, 1100, '0,1000,1100', '用户真实删除', 'system:user:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1109, 1100, '0,1000,1100', '用户导入', 'system:user:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1110, 1100, '0,1000,1100', '用户导出', 'system:user:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:50:15', '2021-07-25 18:50:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1111, 1100, '0,1000,1100', '用户状态改变', 'system:user:changeStatus', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:53:02', '2021-07-25 18:53:02', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1112, 1100, '0,1000,1100', '用户初始化密码', 'system:user:initUserPassword', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 18:55:55', '2021-07-25 18:55:55', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1113, 1100, '0,1000,1100', '更新用户缓存', 'system:user:cache', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-08 18:30:57', '2021-08-08 18:30:57', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1114, 1100, '0,1000,1100', '设置用户首页', 'system:user:homePage', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-08 18:34:30', '2021-08-08 18:34:30', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1200, 1000, '0,1000', '菜单管理', 'system:menu', 'icon-menu', 'menu', 'system/menu/index', NULL, '2', 'M', 1, 96, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1201, 1200, '0,1000,1200', '菜单列表', 'system:menu:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1202, 1200, '0,1000,1200', '菜单回收站', 'system:menu:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1203, 1200, '0,1000,1200', '菜单保存', 'system:menu:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1204, 1200, '0,1000,1200', '菜单更新', 'system:menu:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1205, 1200, '0,1000,1200', '菜单删除', 'system:menu:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1206, 1200, '0,1000,1200', '菜单读取', 'system:menu:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1207, 1200, '0,1000,1200', '菜单恢复', 'system:menu:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1208, 1200, '0,1000,1200', '菜单真实删除', 'system:menu:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1209, 1200, '0,1000,1200', '菜单导入', 'system:menu:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1210, 1200, '0,1000,1200', '菜单导出', 'system:menu:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:01:47', '2021-07-25 19:01:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1300, 1000, '0,1000', '部门管理', 'system:dept', 'ma-icon-dept', 'dept', 'system/dept/index', NULL, '2', 'M', 1, 97, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1301, 1300, '0,1000,1300', '部门列表', 'system:dept:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1302, 1300, '0,1000,1300', '部门回收站', 'system:dept:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1303, 1300, '0,1000,1300', '部门保存', 'system:dept:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1304, 1300, '0,1000,1300', '部门更新', 'system:dept:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1305, 1300, '0,1000,1300', '部门删除', 'system:dept:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1306, 1300, '0,1000,1300', '部门读取', 'system:dept:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1307, 1300, '0,1000,1300', '部门恢复', 'system:dept:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1308, 1300, '0,1000,1300', '部门真实删除', 'system:dept:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1309, 1300, '0,1000,1300', '部门导入', 'system:dept:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1310, 1300, '0,1000,1300', '部门导出', 'system:dept:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:16:33', '2021-07-25 19:16:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1400, 1000, '0,1000', '角色管理', 'system:role', 'ma-icon-role', 'role', 'system/role/index', NULL, '2', 'M', 1, 98, NULL, NULL, '2021-07-25 19:17:37', '2021-07-25 19:17:37', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1401, 1400, '0,1000,1400', '角色列表', 'system:role:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:37', '2021-07-25 19:17:37', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1402, 1400, '0,1000,1400', '角色回收站', 'system:role:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1403, 1400, '0,1000,1400', '角色保存', 'system:role:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1404, 1400, '0,1000,1400', '角色更新', 'system:role:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1405, 1400, '0,1000,1400', '角色删除', 'system:role:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1406, 1400, '0,1000,1400', '角色读取', 'system:role:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1407, 1400, '0,1000,1400', '角色恢复', 'system:role:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1408, 1400, '0,1000,1400', '角色真实删除', 'system:role:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1409, 1400, '0,1000,1400', '角色导入', 'system:role:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1410, 1400, '0,1000,1400', '角色导出', 'system:role:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 19:17:38', '2021-07-25 19:17:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1411, 1400, '0,1000,1400', '角色状态改变', 'system:role:changeStatus', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-30 11:21:24', '2021-07-30 11:21:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1412, 1400, '0,1000,1400', '更新菜单权限', 'system:role:menuPermission', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-09 11:52:33', '2021-08-09 11:52:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1413, 1400, '0,1000,1400', '更新数据权限', 'system:role:dataPermission', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-09 11:52:52', '2021-08-09 11:52:52', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1500, 1000, '0,1000', '岗位管理', 'system:post', 'ma-icon-post', 'post', 'system/post/index', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1501, 1500, '0,1000,1500', '岗位列表', 'system:post:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1502, 1500, '0,1000,1500', '岗位回收站', 'system:post:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1503, 1500, '0,1000,1500', '岗位保存', 'system:post:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1504, 1500, '0,1000,1500', '岗位更新', 'system:post:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1505, 1500, '0,1000,1500', '岗位删除', 'system:post:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1506, 1500, '0,1000,1500', '岗位读取', 'system:post:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1507, 1500, '0,1000,1500', '岗位恢复', 'system:post:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1508, 1500, '0,1000,1500', '岗位真实删除', 'system:post:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1509, 1500, '0,1000,1500', '岗位导入', 'system:post:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1510, 1500, '0,1000,1500', '岗位导出', 'system:post:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-25 20:46:38', '2021-07-25 20:46:38', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2000, 0, '0', '数据', 'dataCenter', 'icon-storage', 'dataCenter', '', NULL, '2', 'M', 1, 98, NULL, NULL, '2021-07-31 17:17:03', '2021-07-31 17:17:03', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2100, 2000, '0,2000', '数据字典', 'system:dict', 'ma-icon-dict', 'dict', 'system/dict/index', NULL, '2', 'M', 1, 99, NULL, NULL, '2021-07-31 18:33:45', '2021-07-31 18:33:45', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2101, 2100, '0,2000,2100', '数据字典列表', 'system:dict:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:45', '2021-07-31 18:33:45', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2102, 2100, '0,2000,2100', '数据字典回收站', 'system:dict:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:45', '2021-07-31 18:33:45', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2103, 2100, '0,2000,2100', '数据字典保存', 'system:dict:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:45', '2021-07-31 18:33:45', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2104, 2100, '0,2000,2100', '数据字典更新', 'system:dict:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:45', '2021-07-31 18:33:45', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2105, 2100, '0,2000,2100', '数据字典删除', 'system:dict:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:45', '2021-07-31 18:33:45', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2106, 2100, '0,2000,2100', '数据字典读取', 'system:dict:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:46', '2021-07-31 18:33:46', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2107, 2100, '0,2000,2100', '数据字典恢复', 'system:dict:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:46', '2021-07-31 18:33:46', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2108, 2100, '0,2000,2100', '数据字典真实删除', 'system:dict:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:46', '2021-07-31 18:33:46', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2109, 2100, '0,2000,2100', '数据字典导入', 'system:dict:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:46', '2021-07-31 18:33:46', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2110, 2100, '0,2000,2100', '数据字典导出', 'system:dict:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:33:46', '2021-07-31 18:33:46', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2200, 2000, '0,2000', '附件管理', 'system:attachment', 'ma-icon-attach', 'attachment', 'system/attachment/index', NULL, '2', 'M', 1, 98, NULL, NULL, '2021-07-31 18:36:34', '2021-07-31 18:36:34', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2201, 2300, '0,2000,2200', '附件删除', 'system:attachment:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:37:20', '2021-07-31 18:37:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2202, 2200, '0,2000,2200', '附件列表', 'system:attachment:index', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:38:05', '2021-07-31 18:38:05', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2203, 2200, '0,2000,2200', '附件回收站', 'system:attachment:recycle', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:38:57', '2021-07-31 18:38:57', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2204, 2200, '0,2000,2200', '附件真实删除', 'system:attachment:realDelete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:40:44', '2021-07-31 18:40:44', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2300, 2000, '0,2000', '数据表维护', 'system:dataMaintain', 'ma-icon-db', 'dataMaintain', 'system/dataMaintain/index', NULL, '2', 'M', 1, 95, NULL, NULL, '2021-07-31 18:43:20', '2021-07-31 18:43:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2301, 2300, '0,2000,2300', '数据表列表', 'system:dataMaintain:index', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:44:03', '2021-07-31 18:44:03', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2302, 2300, '0,2000,2300', '数据表详细', 'system:dataMaintain:detailed', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:45:17', '2021-07-31 18:45:17', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2303, 2300, '0,2000,2300', '数据表清理碎片', 'system:dataMaintain:fragment', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:46:04', '2021-07-31 18:46:04', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2304, 2300, '0,2000,2300', '数据表优化', 'system:dataMaintain:optimize', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:46:31', '2021-07-31 18:46:31', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3000, 0, '0', '监控', 'monitor', 'icon-desktop', 'monitor', '', NULL, '2', 'M', 1, 97, NULL, NULL, '2021-07-31 18:49:24', '2021-07-31 18:49:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3200, 3000, '0,3000', '服务监控', 'system:monitor:server', 'icon-thunderbolt', 'server', 'system/monitor/server/index', NULL, '2', 'M', 1, 99, NULL, NULL, '2021-07-31 18:52:46', '2021-07-31 18:52:46', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3300, 3000, '0,3000', '日志监控', 'logs', 'icon-book', 'logs', '', NULL, '2', 'M', 1, 95, NULL, NULL, '2021-07-31 18:54:01', '2021-07-31 18:54:01', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3400, 3300, '0,3000,3200', '登录日志', 'system:loginLog', 'icon-idcard', 'loginLog', 'system/logs/loginLog', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-07-31 18:54:55', '2021-07-31 18:54:55', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3401, 3400, '0,3000,3200,3300', '登录日志删除', 'system:loginLog:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:56:43', '2021-07-31 18:56:43', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3500, 3300, '0,3000,3200', '操作日志', 'system:operLog', 'icon-robot', 'operLog', 'system/logs/operLog', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-07-31 18:55:40', '2021-07-31 18:55:40', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3501, 3500, '0,3000,3200,3400', '操作日志删除', 'system:operLog:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 18:56:19', '2021-07-31 18:56:19', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3600, 3000, '0,3000', '在线用户', 'system:onlineUser', 'ma-icon-online', 'onlineUser', 'system/monitor/onlineUser/index', NULL, '2', 'M', 1, 98, NULL, NULL, '2021-08-08 18:26:31', '2021-08-08 18:26:31', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3601, 3500, '0,3000,3500', '在线用户列表', 'system:onlineUser:index', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-08 18:26:55', '2021-08-08 18:26:55', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3602, 3500, '0,3000,3500', '强退用户', 'system:onlineUser:kick', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-08 18:27:21', '2021-08-08 18:27:21', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4000, 0, '0', '工具', 'devTools', 'ma-icon-tool', 'devTools', '', NULL, '2', 'M', 1, 95, NULL, NULL, '2021-07-31 19:03:32', '2021-07-31 19:03:32', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4100, 4000, '0,4000', '模块管理', 'setting:module', 'icon-folder', 'module', 'setting/module/index', NULL, '2', 'M', 1, 99, NULL, NULL, '2021-07-31 19:33:49', '2021-07-31 19:33:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4101, 4100, '0,4000,4100', '新增模块', 'setting:module:save', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:45:29', '2021-07-31 19:45:29', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4102, 4100, '0,4000,4100', '模块删除', 'setting:module:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:34:15', '2021-07-31 19:34:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4103, 4100, '0,4000,4100', '模块列表', 'setting:module:index', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-09 11:48:27', '2021-08-09 11:48:27', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4200, 4000, '0,4000', '代码生成器', 'setting:code', 'ma-icon-code', 'code', 'setting/code/index', NULL, '2', 'M', 1, 98, NULL, NULL, '2021-07-31 19:36:17', '2021-07-31 19:36:17', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4201, 4200, '0,4000,4200', '预览代码', 'setting:code:preview', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:36:44', '2021-07-31 19:36:44', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4202, 4200, '0,4000,4200', '装载数据表', 'setting:code:loadTable', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:38:03', '2021-07-31 19:38:03', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4203, 4200, '0,4000,4200', '删除业务表', 'setting:code:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:38:53', '2021-07-31 19:38:53', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4204, 4200, '0,4000,4200', '同步业务表', 'setting:code:sync', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:39:18', '2021-07-31 19:39:18', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4205, 4200, '0,4000,4200', '生成代码', 'setting:code:generate', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:39:48', '2021-07-31 19:39:48', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4400, 4000, '0,4000', '定时任务', 'setting:crontab', 'icon-schedule', 'crontab', 'setting/crontab/index', '', '2', 'M', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4401, 4400, '0,4000,4400', '定时任务列表', 'setting:crontab:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4402, 4400, '0,4000,4400', '定时任务保存', 'setting:crontab:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4403, 4400, '0,4000,4400', '定时任务更新', 'setting:crontab:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4404, 4400, '0,4000,4400', '定时任务删除', 'setting:crontab:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4405, 4400, '0,4000,4400', '定时任务读取', 'setting:crontab:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4406, 4400, '0,4000,4400', '定时任务导入', 'setting:crontab:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4407, 4400, '0,4000,4400', '定时任务导出', 'setting:crontab:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-07-31 19:47:49', '2021-07-31 19:47:49', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4408, 4400, '0,4000,4400', '定时任务执行', 'setting:crontab:run', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-07 23:44:06', '2021-08-07 23:44:06', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4500, 0, '0', '系统设置', 'setting:config', 'icon-settings', 'system', 'setting/config/index', '', '2', 'M', 1, 0, NULL, NULL, '2021-07-31 19:58:57', '2021-07-31 19:58:57', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4502, 4500, '0,4500', '配置列表', 'setting:config:index', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-10 13:09:18', '2021-08-10 13:09:18', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4504, 4500, '0,4500', '新增配置 ', 'setting:config:save', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-10 13:11:56', '2021-08-10 13:11:56', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4505, 4500, '0,4500', '更新配置', 'setting:config:update', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-10 13:12:25', '2021-08-10 13:12:25', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4506, 4500, '0,4500', '删除配置', 'setting:config:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-10 13:13:33', '2021-08-10 13:13:33', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4507, 4500, '0,4500', '清除配置缓存', 'setting:config:clearCache', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-08-10 13:13:59', '2021-08-10 13:13:59', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2500, 2000, '0,2000', '应用中心', 'apps', 'icon-apps', 'apps', '', NULL, '2', 'M', 1, 90, NULL, NULL, '2021-11-11 21:16:47', '2021-11-11 21:16:47', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2510, 2500, '0,2000,2500', '应用分组', 'system:appGroup', 'ma-icon-group', 'appGroup', 'system/appGroup/index', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2511, 2510, '0,2000,2500,2510', '应用分组列表', 'system:appGroup:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2512, 2510, '0,2000,2500,2510', '应用分组回收站', 'system:appGroup:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2513, 2510, '0,2000,2500,2510', '应用分组保存', 'system:appGroup:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2514, 2510, '0,2000,2500,2510', '应用分组更新', 'system:appGroup:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2515, 2510, '0,2000,2500,2510', '应用分组删除', 'system:appGroup:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2516, 2510, '0,2000,2500,2510', '应用分组读取', 'system:appGroup:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2517, 2510, '0,2000,2500,2510', '应用分组恢复', 'system:appGroup:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2518, 2510, '0,2000,2500,2510', '应用分组真实删除', 'system:appGroup:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2519, 2510, '0,2000,2500,2510', '应用分组导入', 'system:appGroup:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2520, 2510, '0,2000,2500,2510', '应用分组导出', 'system:appGroup:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:31:28', '2021-11-11 21:31:28', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2530, 2500, '0,2000,2500', '应用管理', 'system:app', 'icon-archive', 'app', 'system/app/index', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2531, 2530, '0,2000,2500,2530', '应用列表', 'system:app:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2532, 2530, '0,2000,2500,2530', '应用回收站', 'system:app:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2533, 2530, '0,2000,2500,2530', '应用保存', 'system:app:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2534, 2530, '0,2000,2500,2530', '应用更新', 'system:app:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2535, 2530, '0,2000,2500,2530', '应用删除', 'system:app:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2536, 2530, '0,2000,2500,2530', '应用读取', 'system:app:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2537, 2530, '0,2000,2500,2530', '应用恢复', 'system:app:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2538, 2530, '0,2000,2500,2530', '应用真实删除', 'system:app:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2539, 2530, '0,2000,2500,2530', '应用导入', 'system:app:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2540, 2530, '0,2000,2500,2530', '应用导出', 'system:app:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:35:35', '2021-11-11 21:35:35', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2541, 2530, '0,2000,2500,2530', '应用绑定接口', 'system:app:bind', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2600, 2000, '0,2000', '应用接口', 'apis', 'icon-common', 'apis', '', NULL, '2', 'M', 1, 80, NULL, NULL, '2021-11-11 21:23:57', '2021-11-11 21:23:57', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2610, 2600, '0,2000,2600', '接口分组', 'system:apiGroup', 'ma-icon-group', 'apiGroup', 'system/apiGroup/index', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2611, 2610, '0,2000,2600,2610', '接口分组列表', 'system:apiGroup:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2612, 2610, '0,2000,2600,2610', '接口分组回收站', 'system:apiGroup:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2613, 2610, '0,2000,2600,2610', '接口分组保存', 'system:apiGroup:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2614, 2610, '0,2000,2600,2610', '接口分组更新', 'system:apiGroup:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2615, 2610, '0,2000,2600,2610', '接口分组删除', 'system:apiGroup:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2616, 2610, '0,2000,2600,2610', '接口分组读取', 'system:apiGroup:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2617, 2610, '0,2000,2600,2610', '接口分组恢复', 'system:apiGroup:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2618, 2610, '0,2000,2600,2610', '接口分组真实删除', 'system:apiGroup:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2619, 2610, '0,2000,2600,2610', '接口分组导入', 'system:apiGroup:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2620, 2610, '0,2000,2600,2610', '接口分组导出', 'system:apiGroup:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:37:54', '2021-11-11 21:37:54', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2630, 2600, '0,2000,2600', '接口管理', 'system:api', 'icon-mind-mapping', 'api', 'system/api/index', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2631, 2630, '0,2000,2600,2630', '接口列表', 'system:api:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2632, 2630, '0,2000,2600,2630', '接口回收站', 'system:api:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2633, 2630, '0,2000,2600,2630', '接口保存', 'system:api:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2634, 2630, '0,2000,2600,2630', '接口更新', 'system:api:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2635, 2630, '0,2000,2600,2630', '接口删除', 'system:api:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2636, 2630, '0,2000,2600,2630', '接口读取', 'system:api:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2637, 2630, '0,2000,2600,2630', '接口恢复', 'system:api:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2638, 2630, '0,2000,2600,2630', '接口真实删除', 'system:api:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2639, 2630, '0,2000,2600,2630', '接口导入', 'system:api:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2640, 2630, '0,2000,2600,2630', '接口导出', 'system:api:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-11 21:44:24', '2021-11-11 21:44:24', NULL, NULL)", + + "INSERT INTO `{$model}` VALUES (3800, 3300, '0,3000,3300', '接口日志', 'system:apiLog', 'icon-calendar', 'apiLog', 'system/logs/apiLog', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-12-06 21:50:05', '2021-12-06 21:50:05', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3801, 3800, '0,3000,3300,3800', '接口日志删除', 'system:apiLog:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-06 21:50:40', '2021-12-06 21:50:40', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4409, 4400, '0,4000,4400', '定时任务日志删除', 'setting:crontab:deleteLog', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-06 22:06:12', '2021-12-06 22:06:12', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3700, 3000, '0,3000', '缓存监控', 'system:cache', 'icon-dice', 'cache', 'system/monitor/cache/index', NULL, '2', 'M', 1, 98, NULL, NULL, '2021-10-26 20:50:31', '2021-10-26 20:50:31', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3701, 3700, '0,3000,3700', '获取Redis信息', 'system:cache:monitor', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-10-26 20:50:31', '2021-10-26 20:50:31', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3702, 3700, '0,3000,3700', '删除一个缓存', 'system:cache:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-10-26 20:50:31', '2021-10-26 20:50:31', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3703, 3700, '0,3000,3700', '清空所有缓存', 'system:cache:clear', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-10-26 20:50:31', '2021-10-26 20:50:31', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1311, 1300, '0,1000,1300', '部门状态改变', 'system:dept:changeStatus', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-09 18:26:15', '2021-11-09 18:26:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (1511, 1500, '0,1000,1500', '岗位状态改变', 'system:post:changeStatus', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-09 18:26:15', '2021-11-09 18:26:15', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2112, 2100, '0,2000,2100', '字典状态改变', 'system:dataDict:changeStatus', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-11-09 18:26:15', '2021-11-09 18:26:15', NULL, NULL)", + + "INSERT INTO `{$model}` VALUES (3850, 3300, '0,3000,3300', '队列日志', 'system:queueLog', 'icon-layers', 'queueLog', 'system/logs/queueLog', NULL, '2', 'M', 1, 0, NULL, NULL, '2021-12-25 16:41:14', '2021-12-25 16:41:14', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3851, 3850, '0,3000,3300,3850', '删除队列日志', 'system:queueLog:delete', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 16:42:42', '2021-12-25 16:42:42', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (3852, 3850, '0,3000,3300,3850', '更新队列状态', 'system:queueLog:updateStatus', '', NULL, '', NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 16:45:03', '2021-12-25 16:47:16', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2700, 2000, '0,2000', '系统公告', 'system:notice', 'icon-bulb', 'notice', 'system/notice/index', NULL, '2', 'M', 1, 94, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2701, 2700, '0,2000,2700', '系统公告列表', 'system:notice:index', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2702, 2700, '0,2000,2700', '系统公告回收站', 'system:notice:recycle', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2703, 2700, '0,2000,2700', '系统公告保存', 'system:notice:save', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2704, 2700, '0,2000,2700', '系统公告更新', 'system:notice:update', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2705, 2700, '0,2000,2700', '系统公告删除', 'system:notice:delete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2706, 2700, '0,2000,2700', '系统公告读取', 'system:notice:read', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2707, 2700, '0,2000,2700', '系统公告恢复', 'system:notice:recovery', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2708, 2700, '0,2000,2700', '系统公告真实删除', 'system:notice:realDelete', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2709, 2700, '0,2000,2700', '系统公告导入', 'system:notice:import', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (2710, 2700, '0,2000,2700', '系统公告导出', 'system:notice:export', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2021-12-25 18:10:20', '2021-12-25 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4104, 4100, '0,4000,4100', '模块启停用', 'setting:module:status', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2022-02-13 18:10:20', '2022-02-13 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4105, 4100, '0,4000,4100', '安装模块', 'setting:module:install', NULL, NULL, NULL, NULL, '1', 'B', 1, 0, NULL, NULL, '2022-02-13 18:10:20', '2022-02-13 18:10:20', NULL, NULL)", + "INSERT INTO `{$model}` VALUES (4600, 4000, '0,4000', '系统接口', 'systemInterface', 'icon-compass', 'systemInterface', 'setting/systemInterface/index', NULL, '2', 'M', 1, 0, NULL, NULL, '2022-03-30 10:00:28', '2022-03-30 10:00:28', NULL, NULL)", + ]; + } +} diff --git a/app/System/Dto/ApiColumnDto.php b/app/System/Dto/ApiColumnDto.php new file mode 100644 index 0000000..d01818e --- /dev/null +++ b/app/System/Dto/ApiColumnDto.php @@ -0,0 +1,34 @@ +getFilePath($event->getModel()); + try { + $event->getFilesystem()->delete($filePath); + } catch (FileNotFoundException $e) { + // 文件删除失败,跳过删除数据 + $event->setConfirm(false); + } + } + + /** + * 获取文件路径 + * @param SystemUploadfile $model + * @return string + */ + public function getFilePath(SystemUploadfile $model): string + { + return $model->storage_path.'/'.$model->object_name; + } + +} \ No newline at end of file diff --git a/app/System/Listener/LoginListener.php b/app/System/Listener/LoginListener.php new file mode 100644 index 0000000..1d63915 --- /dev/null +++ b/app/System/Listener/LoginListener.php @@ -0,0 +1,128 @@ +get(MineRequest::class); + $service = container()->get(SystemLoginLogService::class); + $redis = redis(); + + $agent = $request->getHeader('user-agent')[0]; + $ip = $request->ip(); + $service->save([ + 'username' => $event->userinfo['username'], + 'ip' => $ip, + 'ip_location' => Str::ipToRegion($ip), + 'os' => $this->os($agent), + 'browser' => $this->browser($agent), + 'status' => $event->loginStatus ? SystemLoginLog::SUCCESS : SystemLoginLog::FAIL, + 'message' => $event->message, + 'login_time' => date('Y-m-d H:i:s') + ]); + + $key = sprintf("%sToken:%s", config('cache.default.prefix'), $event->userinfo['id']); + + $redis->del($key); + ($event->loginStatus && $event->token) && $redis->set( $key, $event->token, config('jwt.ttl') ); + + if ($event->loginStatus) { + $event->userinfo['login_ip'] = $ip; + $event->userinfo['login_time'] = date('Y-m-d H:i:s'); + + SystemUser::query()->where('id', $event->userinfo['id'])->update([ + 'login_ip' => $ip, + 'login_time' => date('Y-m-d H:i:s'), + ]); + } + } + + /** + * @param $agent + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + private function os($agent): string + { + if (false !== stripos($agent, 'win') && preg_match('/nt 6.1/i', $agent)) { + return 'Windows 7'; + } + if (false !== stripos($agent, 'win') && preg_match('/nt 6.2/i', $agent)) { + return 'Windows 8'; + } + if(false !== stripos($agent, 'win') && preg_match('/nt 10.0/i', $agent)) { + return 'Windows 10'; + } + if(false !== stripos($agent, 'win') && preg_match('/nt 11.0/i', $agent)) { + return 'Windows 11'; + } + if (false !== stripos($agent, 'win') && preg_match('/nt 5.1/i', $agent)) { + return 'Windows XP'; + } + if (false !== stripos($agent, 'linux')) { + return 'Linux'; + } + if (false !== stripos($agent, 'mac')) { + return 'Mac'; + } + return t('jwt.unknown'); + } + + /** + * @param $agent + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + private function browser($agent): string + { + if (false !== stripos($agent, "MSIE")) { + return 'MSIE'; + } + if (false !== stripos($agent, "Edg")) { + return 'Edge'; + } + if (false !== stripos($agent, "Chrome")) { + return 'Chrome'; + } + if (false !== stripos($agent, "Firefox")) { + return 'Firefox'; + } + if (false !== stripos($agent, "Safari")) { + return 'Safari'; + } + if (false !== stripos($agent, "Opera")) { + return 'Opera'; + } + return t('jwt.unknown'); + } +} \ No newline at end of file diff --git a/app/System/Listener/UserDeleteListener.php b/app/System/Listener/UserDeleteListener.php new file mode 100644 index 0000000..4a6f302 --- /dev/null +++ b/app/System/Listener/UserDeleteListener.php @@ -0,0 +1,47 @@ +ids as $uid) { + $token = $redis->get($prefix . $uid); + $token && $user->getJwt()->logout($token); + $redis->del([$prefix . $uid]); + } + } +} \ No newline at end of file diff --git a/app/System/Mapper/SettingConfigGroupMapper.php b/app/System/Mapper/SettingConfigGroupMapper.php new file mode 100644 index 0000000..7e3a54b --- /dev/null +++ b/app/System/Mapper/SettingConfigGroupMapper.php @@ -0,0 +1,34 @@ +model = SettingConfigGroup::class; + } + + /** + * 删除组和所属配置 + * @param int $id + * @return bool + * @throws \Exception + */ + public function deleteGroupAndConfig(int $id): bool + { + /* @var $model SettingConfigGroup */ + $model = $this->read($id); + $model->configs()->delete(); + return $model->delete(); + } +} \ No newline at end of file diff --git a/app/System/Mapper/SettingConfigMapper.php b/app/System/Mapper/SettingConfigMapper.php new file mode 100644 index 0000000..cee4118 --- /dev/null +++ b/app/System/Mapper/SettingConfigMapper.php @@ -0,0 +1,85 @@ +model = SettingConfig::class; + } + + /** + * 按Key获取配置 + * @param string $key + * @return array + */ + public function getConfigByKey(string $key): array + { + $model = $this->model::query()->find($key, [ + 'group_id', 'name', 'key', 'value', 'sort', 'input_type', 'config_select_data' + ]); + return $model ? $model->toArray() : []; + } + + /** + * 更新配置 + * @param string $key + * @param array $data + * @return bool + */ + public function updateConfig(string $key, array $data): bool + { + return $this->model::query()->where('key', $key)->update($data) > 0; + } + + /** + * 按 keys 更新配置 + * @param string $key + * @param string|int|bool $value + * @return bool + */ + public function updateByKey(string $key, string|int|bool|null $value = null): bool + { + return $this->model::query()->where('key', $key)->update(['value' => $value]) > 0; + } + + /** + * 保存配置 + * @param array $data + * @return int + */ + public function save(array $data): int + { + $this->filterExecuteAttributes($data); + $model = $this->model::create($data); + return ($model->{$model->getKeyName()}) ? 1 : 0; + } + + /** + * 搜索处理器 + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['group_id']) && !empty($params['group_id'])) { + $query->where('group_id', $params['group_id']); + } + if (isset($params['name']) && !empty($params['name'])) { + $query->where('name', $params['name']); + } + if (isset($params['key']) && !empty($params['key'])) { + $query->where('key', 'like', '%'.$params['key'].'%'); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SettingCrontabLogMapper.php b/app/System/Mapper/SettingCrontabLogMapper.php new file mode 100644 index 0000000..1c6065a --- /dev/null +++ b/app/System/Mapper/SettingCrontabLogMapper.php @@ -0,0 +1,37 @@ +model = SettingCrontabLog::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if ($params['crontab_id'] ?? false) { + $query->where('crontab_id', $params['crontab_id']); + } + return $query; + } + +} \ No newline at end of file diff --git a/app/System/Mapper/SettingCrontabMapper.php b/app/System/Mapper/SettingCrontabMapper.php new file mode 100644 index 0000000..40e703e --- /dev/null +++ b/app/System/Mapper/SettingCrontabMapper.php @@ -0,0 +1,66 @@ +model = SettingCrontab::class; + } + + /** + * @param array $ids + * @return bool + * @throws \Exception + */ + #[Transaction] + public function delete(array $ids): bool + { + foreach ($ids as $id) { + $model = $this->model::find($id); + if ($model) { + $model->logs()->delete(); + $model->delete(); + } + } + return true; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['name'])) { + $query->where('name', 'like', '%'.$params['name'].'%'); + } + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + if (isset($params['type'])) { + $query->where('type', $params['type']); + } + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SettingGenerateColumnsMapper.php b/app/System/Mapper/SettingGenerateColumnsMapper.php new file mode 100644 index 0000000..1d9fab6 --- /dev/null +++ b/app/System/Mapper/SettingGenerateColumnsMapper.php @@ -0,0 +1,40 @@ +model = SettingGenerateColumns::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if ($params['table_id'] ?? false) { + $query->where('table_id', (int) $params['table_id']); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SettingGenerateTablesMapper.php b/app/System/Mapper/SettingGenerateTablesMapper.php new file mode 100644 index 0000000..bbc6879 --- /dev/null +++ b/app/System/Mapper/SettingGenerateTablesMapper.php @@ -0,0 +1,64 @@ +model = SettingGenerateTables::class; + } + + /** + * 删除业务信息表和字段信息表 + * @throws \Exception + */ + #[Transaction] + public function delete(array $ids): bool + { + /* @var SettingGenerateTables $model */ + foreach ($this->model::query()->whereIn('id', $ids)->get() as $model) { + if ($model) { + $model->columns()->delete(); + $model->delete(); + } + } + return true; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['table_name'])) { + $query->where('table_name', 'like', '%'.$params['table_name'].'%'); + } + if (isset($params['minDate']) && isset($params['maxDate'])) { + $query->whereBetween( + 'created_at', + [$params['minDate'] . ' 00:00:00', $params['maxDate'] . ' 23:59:59'] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemApiColumnMapper.php b/app/System/Mapper/SystemApiColumnMapper.php new file mode 100644 index 0000000..1631268 --- /dev/null +++ b/app/System/Mapper/SystemApiColumnMapper.php @@ -0,0 +1,64 @@ +model = SystemApiColumn::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + // 接口ID + if (isset($params['api_id'])) { + $query->where('api_id', '=', $params['api_id']); + } + + // 字段类型 + if (isset($params['type'])) { + $query->where('type', '=', $params['type']); + } + + // 字段名称 + if (isset($params['name'])) { + $query->where('name', '=', $params['name']); + } + + // 数据类型 + if (isset($params['data_type'])) { + $query->where('data_type', '=', $params['data_type']); + } + + // 是否必填 1 非必填 2 必填 + if (isset($params['is_required'])) { + $query->where('is_required', '=', $params['is_required']); + } + + // 状态 (1正常 2停用) + if (isset($params['status'])) { + $query->where('status', '=', $params['status']); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemApiGroupMapper.php b/app/System/Mapper/SystemApiGroupMapper.php new file mode 100644 index 0000000..b6d6932 --- /dev/null +++ b/app/System/Mapper/SystemApiGroupMapper.php @@ -0,0 +1,52 @@ +model = SystemApiGroup::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + // 应用组名称 + if (isset($params['name'])) { + $query->where('name', '=', $params['name']); + } + + // 状态 + if (isset($params['status'])) { + $query->where('status', '=', $params['status']); + } + + // 关联查询api列表 + if (isset($params['getApiList']) && $params['getApiList'] == true) { + $query->with(['apis' => function($query) { + $query->where('status', SystemApi::ENABLE)->select(['id', 'group_id', 'name', 'access_name']); + }]); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemApiLogMapper.php b/app/System/Mapper/SystemApiLogMapper.php new file mode 100644 index 0000000..ed62027 --- /dev/null +++ b/app/System/Mapper/SystemApiLogMapper.php @@ -0,0 +1,50 @@ +model = SystemApiLog::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['api_name'])) { + $query->where('api_name', 'like', '%'.$params['api_name'].'%'); + } + if (isset($params['ip'])) { + $query->where('ip', 'like', '%'.$params['ip'].'%'); + } + if (isset($params['access_name'])) { + $query->where('access_name', 'like', '%'.$params['access_name'].'%'); + } + if (isset($params['access_time']) && is_array($params['access_time']) && count($params['access_time']) == 2) { + $query->whereBetween( + 'access_time', + [ $params['access_time'][0] . ' 00:00:00', $params['access_time'][1] . ' 23:59:59' ] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemApiMapper.php b/app/System/Mapper/SystemApiMapper.php new file mode 100644 index 0000000..8ad3062 --- /dev/null +++ b/app/System/Mapper/SystemApiMapper.php @@ -0,0 +1,47 @@ +model = SystemApi::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + return $query; + } + + /** + * 通过api获取字段列表 + * @param string $id + * @return array + */ + public function getColumnListByApiId(string $id): array + { + return $this->model::query()->where('id', $id)->with(['apiColumn' => function($query) { + $query->where('status', $this->model::ENABLE); + }])->first()->toArray(); + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemAppGroupMapper.php b/app/System/Mapper/SystemAppGroupMapper.php new file mode 100644 index 0000000..1b4e848 --- /dev/null +++ b/app/System/Mapper/SystemAppGroupMapper.php @@ -0,0 +1,44 @@ +model = SystemAppGroup::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + // 应用组名称 + if (isset($params['name'])) { + $query->where('name', '=', $params['name']); + } + + // 状态 + if (isset($params['status'])) { + $query->where('status', '=', $params['status']); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemAppMapper.php b/app/System/Mapper/SystemAppMapper.php new file mode 100644 index 0000000..a359df3 --- /dev/null +++ b/app/System/Mapper/SystemAppMapper.php @@ -0,0 +1,88 @@ +model = SystemApp::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['app_name'])) { + $query->where('app_name', $params['app_name']); + } + + if (isset($params['app_id'])) { + $query->where('app_id', $params['app_id']); + } + + if (isset($params['group_id'])) { + $query->where('group_id', $params['group_id']); + } + + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + return $query; + } + + /** + * 绑定接口 + * @param int $id + * @param array $ids + * @return bool + */ + public function bind(int $id, array $ids): bool + { + $model = $this->read($id); + $model && $model->apis()->sync($ids); + return true; + } + + /** + * 获取api列表 + * @param int $id + * @return array + */ + public function getApiList(int $appId): array + { + return Db::table('system_app_api')->where('app_id', $appId)->pluck('api_id')->toArray(); + } + + /** + * 通过app_id获取app信息和接口数据 + * @param string $appId + * @return array + */ + public function getAppAndInterfaceList(string $appId): array + { + return $this->model::query()->where('app_id', $appId) + ->with(['apis' => function($query) { + $query->where('status', SystemApp::ENABLE); + }])->first(['id', 'app_id', 'app_secret', 'app_name', 'updated_at', 'description'])->toArray(); + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemDeptMapper.php b/app/System/Mapper/SystemDeptMapper.php new file mode 100644 index 0000000..b827c04 --- /dev/null +++ b/app/System/Mapper/SystemDeptMapper.php @@ -0,0 +1,159 @@ +model = SystemDept::class; + } + + /** + * 获取前端选择树 + * @return array + */ + public function getSelectTree(): array + { + $treeData = $this->model::query()->select(['id', 'parent_id', 'id AS value', 'name AS label']) + ->where('status', $this->model::ENABLE) + ->orderBy('parent_id') + ->orderBy('sort', 'desc') + ->userDataScope() + ->get()->toArray(); + return (new MineCollection())->toTree($treeData, $treeData[0]['parent_id'] ?? 0); + } + + /** + * 获取部门领导列表 + * @param array|null $params + * @return array + */ + public function getLeaderList(?array $params = null): array + { + if (empty($params['dept_id'])) { + throw new MineException('缺少部门ID', 500); + } + $query = Db::table('system_user as u') + ->join('system_dept_leader as dl', 'u.id', '=', 'dl.user_id') + ->where('dl.dept_id', '=', $params['dept_id']); + + if (!empty($params['username'])) { + $query->where('u.username', 'like', '%' . $params['username'] . '%'); + } + + if (!empty($params['nickname'])) { + $query->where('u.nickname', 'like', '%' . $params['nickname'] . '%'); + } + + if (!empty($params['status'])) { + $query->where('u.status', $params['status']); + } + + return $this->setPaginate( + $query->paginate( + (int) $params['pageSize'] ?? $this->model::PAGE_SIZE, ['u.*', 'dl.created_at as leader_add_time'], 'page', (int) $params['page'] ?? 1 + ) + ); + } + + /** + * 新增部门领导 + * @param int $id + * @param array $users + * @return bool + */ + #[Transaction] + public function addLeader(int $id, array $users): bool + { + $model = $this->model::find($id, ['id']); + foreach ($users as $key => $user) { + if (Db::table('system_dept_leader')->where('dept_id', $id)->where('user_id', $user['user_id'])->exists()) { + unset($users[$key]); + } + } + count($users) > 0 && $model->leader()->sync($users, false); + return true; + } + + /** + * 删除部门领导 + * @param int $id + * @param array $users + * @return bool + */ + #[Transaction] + public function delLeader(int $id, array $users): bool + { + $model = $this->model::find($id, ['id']); + count($users) > 0 && $model->leader()->detach($users); + return true; + } + + /** + * 查询部门名称 + * @param array|null $ids + * @return array + */ + public function getDeptName(array $ids = null): array + { + return $this->model::withTrashed()->whereIn('id', $ids)->pluck('name')->toArray(); + } + + /** + * @param int $id + * @return bool + */ + public function checkChildrenExists(int $id): bool + { + return $this->model::withTrashed()->where('parent_id', $id)->exists(); + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + if (isset($params['name'])) { + $query->where('name', 'like', '%'.$params['name'].'%'); + } + + if (isset($params['leader'])) { + $query->where('leader', $params['leader']); + } + + if (isset($params['phone'])) { + $query->where('phone', $params['phone']); + } + + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemDictDataMapper.php b/app/System/Mapper/SystemDictDataMapper.php new file mode 100644 index 0000000..dd6dd6d --- /dev/null +++ b/app/System/Mapper/SystemDictDataMapper.php @@ -0,0 +1,51 @@ +model = SystemDictData::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['type_id'])) { + $query->where('type_id', $params['type_id']); + } + if (isset($params['code'])) { + $query->where('code', $params['code']); + } + if (isset($params['value'])) { + $query->where('value', 'like', '%'.$params['value'].'%'); + } + if (isset($params['label'])) { + $query->where('label', 'like', '%'.$params['label'].'%'); + } + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemDictTypeMapper.php b/app/System/Mapper/SystemDictTypeMapper.php new file mode 100644 index 0000000..6189f4c --- /dev/null +++ b/app/System/Mapper/SystemDictTypeMapper.php @@ -0,0 +1,76 @@ +model = SystemDictType::class; + } + + /** + * @param int $id + * @param array $data + * @return bool + */ + #[Transaction] + public function update(int $id, array $data): bool + { + parent::update($id, $data); + SystemDictData::where('type_id', $id)->update(['code' => $data['code']]) > 0; + return true; + } + + /** + * @param array $ids + * @return bool + */ + #[Transaction] + public function realDelete(array $ids): bool + { + foreach ($ids as $id) { + $model = $this->model::withTrashed()->find($id); + if ($model) { + $model->dictData()->forceDelete(); + $model->forceDelete(); + } + } + return true; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['code'])) { + $query->where('code', 'like', '%'.$params['code'].'%'); + } + if (isset($params['name'])) { + $query->where('name', 'like', '%'.$params['name'].'%'); + } + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemLoginLogMapper.php b/app/System/Mapper/SystemLoginLogMapper.php new file mode 100644 index 0000000..f761ef0 --- /dev/null +++ b/app/System/Mapper/SystemLoginLogMapper.php @@ -0,0 +1,53 @@ +model = SystemLoginLog::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['ip'])) { + $query->where('ip', $params['ip']); + } + + if (isset($params['username'])) { + $query->where('username', 'like', '%'.$params['username'].'%'); + } + + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + if (isset($params['login_time']) && is_array($params['login_time']) && count($params['login_time']) == 2) { + $query->whereBetween( + 'login_time', + [ $params['login_time'][0] . ' 00:00:00', $params['login_time'][1] . ' 23:59:59' ] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemMenuMapper.php b/app/System/Mapper/SystemMenuMapper.php new file mode 100644 index 0000000..dcbc52b --- /dev/null +++ b/app/System/Mapper/SystemMenuMapper.php @@ -0,0 +1,237 @@ +model = SystemMenu::class; + } + + /** + * 获取超级管理员(创始人)的路由菜单 + * @return array + */ + public function getSuperAdminRouters(): array + { + return $this->model::query() + ->select($this->menuField) + ->where('status', $this->model::ENABLE) + ->orderBy('sort', 'desc') + ->get()->sysMenuToRouterTree(); + } + + /** + * 通过菜单ID列表获取菜单数据 + * @param array $ids + * @return array + */ + public function getRoutersByIds(array $ids): array + { + return $this->model::query() + ->select($this->menuField) + ->whereIn('id', $ids) + ->where('status', $this->model::ENABLE) + ->orderBy('sort', 'desc') + ->get()->sysMenuToRouterTree(); + } + + /** + * 获取前端选择树 + * @param array $data + * @return array + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \RedisException + */ + public function getSelectTree(array $data): array + { + $query = $this->model::query()->select(['id', 'parent_id', 'id AS value', 'name AS label']) + ->where('status', $this->model::ENABLE)->orderBy('sort', 'desc'); + + if ( ($data['scope'] ?? false) && ! user()->isSuperAdmin()) { + $roleData = container()->get(SystemRoleMapper::class)->getMenuIdsByRoleIds( + SystemUser::find(user()->getId(), ['id'])->roles()->pluck('id')->toArray() + ); + + $ids = []; + foreach ($roleData as $val) { + foreach ($val['menus'] as $menu) { + $ids[] = $menu['id']; + } + } + unset($roleData); + $query->whereIn('id', array_unique($ids)); + } + + if (!empty($data['onlyMenu'])) { + $query->where('type', SystemMenu::MENUS_LIST); + } + + return $query->get()->toTree(); + } + + /** + * 查询菜单code + * @param array|null $ids + * @return array + */ + public function getMenuCode(array $ids = null): array + { + return $this->model::query()->whereIn('id', $ids)->pluck('code')->toArray(); + } + + /** + * 通过 code 查询菜单名称 + * @param string $code + * @return string|null + */ + public function findNameByCode(string $code): ?string + { + return $this->model::query()->where('code', $code)->value('name'); + } + + /** + * 单个或批量真实删除数据 + * @param array $ids + * @return bool + */ + #[DeleteCache("loginInfo:*"), Transaction] + public function realDelete(array $ids): bool + { + foreach ($ids as $id) { + $model = $this->model::withTrashed()->find($id); + if ($model) { + $model->roles()->detach(); + $model->forceDelete(); + } + } + return true; + } + + /** + * 新增菜单 + * @param array $data + * @return int + */ + #[DeleteCache("loginInfo:*")] + public function save(array $data): int + { + return parent::save($data); + } + + /** + * 更新菜单 + * @param int $id + * @param array $data + * @return bool + */ + #[DeleteCache("loginInfo:*")] + public function update(int $id, array $data): bool + { + return parent::update($id, $data); + } + + /** + * 逻辑删除菜单 + * @param array $ids + * @return bool + */ + #[DeleteCache("loginInfo:*")] + public function delete(array $ids): bool + { + return parent::delete($ids); + } + + /** + * 通过 route 查询菜单 + * @param string $route + * @return Builder|Model|object|null + */ + public function findMenuByRoute(string $route) + { + return $this->model::query()->where('route', $route)->first(); + } + + /** + * 查询菜单code + * @param array|null $ids + * @return array + */ + public function getMenuName(array $ids = null): array + { + return $this->model::withTrashed()->whereIn('id', $ids)->pluck('name')->toArray(); + } + + /** + * @param int $id + * @return bool + */ + public function checkChildrenExists(int $id): bool + { + return $this->model::withTrashed()->where('parent_id', $id)->exists(); + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + if (isset($params['name'])) { + $query->where('name', 'like', '%'.$params['name'].'%'); + } + + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + + if (isset($params['noButton']) && $params['noButton'] === true) { + $query->where('type', '<>', SystemMenu::BUTTON); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemNoticeMapper.php b/app/System/Mapper/SystemNoticeMapper.php new file mode 100644 index 0000000..b036e75 --- /dev/null +++ b/app/System/Mapper/SystemNoticeMapper.php @@ -0,0 +1,50 @@ +model = SystemNotice::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['title'])) { + $query->where('title', '=', $params['title']); + } + + if (isset($params['type'])) { + $query->where('type', '=', $params['type']); + } + + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemOperLogMapper.php b/app/System/Mapper/SystemOperLogMapper.php new file mode 100644 index 0000000..d95e117 --- /dev/null +++ b/app/System/Mapper/SystemOperLogMapper.php @@ -0,0 +1,49 @@ +model = SystemOperLog::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['ip'])) { + $query->where('ip', $params['ip']); + } + if (isset($params['service_name'])) { + $query->where('service_name', 'like', '%'.$params['service_name'].'%'); + } + if (isset($params['username'])) { + $query->where('username', 'like', '%'.$params['username'].'%'); + } + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + return $query; + } + +} \ No newline at end of file diff --git a/app/System/Mapper/SystemPostMapper.php b/app/System/Mapper/SystemPostMapper.php new file mode 100644 index 0000000..9ed2246 --- /dev/null +++ b/app/System/Mapper/SystemPostMapper.php @@ -0,0 +1,48 @@ +model = SystemPost::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['name'])) { + $query->where('name', 'like', '%'.$params['name'].'%'); + } + if (isset($params['code'])) { + $query->where('code', $params['code']); + } + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemQueueLogMapper.php b/app/System/Mapper/SystemQueueLogMapper.php new file mode 100644 index 0000000..677762a --- /dev/null +++ b/app/System/Mapper/SystemQueueLogMapper.php @@ -0,0 +1,60 @@ +model = SystemQueueLog::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + // 交换机名称 + if (isset($params['exchange_name'])) { + $query->where('exchange_name', '=', $params['exchange_name']); + } + + // 路由名称 + if (isset($params['routing_key_name'])) { + $query->where('routing_key_name', '=', $params['routing_key_name']); + } + + // 队列名称 + if (isset($params['queue_name'])) { + $query->where('queue_name', '=', $params['queue_name']); + } + + // 生产状态 0:未生产 1:生产中 2:生产成功 3:生产失败 4:生产重复 + if (isset($params['produce_status'])) { + $query->where('produce_status', '=', $params['produce_status']); + } + + // 消费状态 0:未消费 1:消费中 2:消费成功 3:消费失败 4:消费重复 + if (isset($params['consume_status'])) { + $query->where('consume_status', '=', $params['consume_status']); + } + + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemQueueMessageMapper.php b/app/System/Mapper/SystemQueueMessageMapper.php new file mode 100644 index 0000000..8c6c76d --- /dev/null +++ b/app/System/Mapper/SystemQueueMessageMapper.php @@ -0,0 +1,157 @@ +model = SystemQueueMessage::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['title'])) { + $query->where('title', 'like', '%'.$params['title'].'%'); + } + + // 内容类型 + if (isset($params['content_type']) && $params['content_type'] !== 'all') { + $query->where('content_type', '=', $params['content_type']); + } + + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) === 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + + // 获取收信数据 + if (isset($params['getReceive'])) { + $query->with(['sendUser' => function($query) { + $query->select([ 'id', 'username', 'nickname', 'avatar' ]); + }]); + $prefix = env('DB_PREFIX'); + $readStatus = $params['read_status'] ?? 'all'; + $sql = << 'all', `read_status` = ?, ' 1 = 1 ') + ) + sql; + $query->whereRaw($sql, [ $params['user_id'] ?? user()->getId(), $readStatus, $readStatus ]); + } + + // 收取发信数据 + if (isset($params['getSend'])) { + $query->where('send_by', user()->getId()); + } + + return $query; + } + + /** + * 获取接收人列表 + * @param int $id + * @return array + */ + public function getReceiveUserList(int $id): array + { + $prefix = env('DB_PREFIX'); + $paginate = Db::table('system_user as u') + ->select(Db::raw("{$prefix}u.username, {$prefix}u.nickname, if ({$prefix}r.read_status = 2, '已读', '未读') as read_status ")) + ->join('system_queue_message_receive as r', 'u.id', '=', 'r.user_id') + ->where('r.message_id', $id) + ->paginate( + $params['pageSize'] ?? $this->model::PAGE_SIZE, + ['*'], + 'page', + $params['page'] ?? 1 + ); + + return $this->setPaginate($paginate); + } + + /** + * 保存数据 + * @param array $data + * @return int + */ + #[Transaction] + public function save(array $data): int + { + $receiveUsers = $data['receive_users']; + $this->filterExecuteAttributes($data); + $model = $this->model::create($data); + $model->receiveUser()->sync($receiveUsers); + return $model->{$model->getKeyName()}; + } + + /** + * 删除消息 + * @param array $ids + * @return bool + * @throws \Exception + */ + #[Transaction] + public function delete(array $ids): bool + { + foreach ($ids as $id) { + $model = $this->model::find($id); + if ($model) { + $model->receiveUser()->detach(); + $model->delete(); + } + } + return true; + } + + /** + * 更新中间表数据状态 + * @param array $ids + * @param string $columnName + * @param string $value + * @return bool + */ + public function updateDataStatus(array $ids, string $columnName = 'read_status', int $value = 2): bool + { + foreach ($ids as $id) { + $result = Db::table('system_queue_message_receive') + ->where('message_id', $id) + ->where('user_id', user()->getId()) + ->value($columnName); + + if ($result != $value) { + Db::table('system_queue_message_receive') + ->where('message_id', $id) + ->where('user_id', user()->getId()) + ->update([ $columnName => $value ]); + } + } + + return true; + } +} diff --git a/app/System/Mapper/SystemRoleMapper.php b/app/System/Mapper/SystemRoleMapper.php new file mode 100644 index 0000000..7eca911 --- /dev/null +++ b/app/System/Mapper/SystemRoleMapper.php @@ -0,0 +1,187 @@ +model = SystemRole::class; + } + + /** + * 通过角色ID列表获取菜单ID + * @param array $ids + * @return array + */ + public function getMenuIdsByRoleIds(array $ids): array + { + if (empty($ids)) return []; + + return $this->model::query()->whereIn('id', $ids)->with(['menus' => function($query) { + $query->select('id')->where('status', $this->model::ENABLE)->orderBy('sort', 'desc'); + }])->get(['id'])->toArray(); + } + + /** + * 通过角色ID列表获取部门ID + * @param array $ids + * @return array + */ + public function getDeptIdsByRoleIds(array $ids): array + { + if (empty($ids)) return []; + + return $this->model::query()->whereIn('id', $ids)->with(['depts' => function($query) { + $query->select('id')->where('status', $this->model::ENABLE)->orderBy('sort', 'desc'); + }])->get(['id'])->toArray(); + } + + /** + * 通过 code 查询角色名称 + * @param string $code + * @return string + */ + public function findNameByCode(string $code): ?string + { + return $this->model::query()->where('code', $code)->value('name'); + } + + /** + * 检查角色code是否已存在 + * @param string $code + * @return bool + */ + public function checkRoleCode(string $code): bool + { + return $this->model::query()->where('code', $code)->exists(); + } + + /** + * 新建角色 + * @param array $data + * @return int + */ + #[Transaction] + public function save(array $data): int + { + $menuIds = $data['menu_ids'] ?? []; + $deptIds = $data['dept_ids'] ?? []; + $this->filterExecuteAttributes($data); + + $role = $this->model::create($data); + empty($menuIds) || $role->menus()->sync(array_unique($menuIds), false); + empty($deptIds) || $role->depts()->sync($deptIds, false); + return $role->id; + } + + /** + * 更新角色 + * @param int $id + * @param array $data + * @return bool + */ + #[DeleteCache("loginInfo:*"), Transaction] + public function update(int $id, array $data): bool + { + $menuIds = $data['menu_ids'] ?? []; + $deptIds = $data['dept_ids'] ?? []; + $this->filterExecuteAttributes($data, true); + $this->model::query()->where('id', $id)->update($data); + if ($id != env('ADMIN_ROLE')) { + $role = $this->model::find($id); + if ($role) { + !empty($menuIds) && $role->menus()->sync(array_unique($menuIds)); + !empty($deptIds) && $role->depts()->sync($deptIds); + return true; + } + } + return false; + } + + /** + * 单个或批量软删除数据 + * @param array $ids + * @return bool + */ + #[DeleteCache("loginInfo:*")] + public function delete(array $ids): bool + { + $adminId = env('ADMIN_ROLE'); + if (in_array($adminId, $ids)) { + unset($ids[array_search($adminId, $ids)]); + } + $this->model::destroy($ids); + return true; + } + + /** + * 批量真实删除角色 + * @param array $ids + * @return bool + */ + #[DeleteCache("loginInfo:*"), Transaction] + public function realDelete(array $ids): bool + { + foreach ($ids as $id) { + if ($id == env('ADMIN_ROLE')) { + continue; + } + $role = $this->model::withTrashed()->find($id); + if ($role) { + // 删除关联菜单 + $role->menus()->detach(); + // 删除关联部门 + $role->depts()->detach(); + // 删除关联用户 + $role->users()->detach(); + // 删除角色数据 + $role->forceDelete(); + } + } + return true; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['name'])) { + $query->where('name', 'like', '%'.$params['name'].'%'); + } + if (isset($params['code'])) { + $query->where('code', $params['code']); + } + + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + if (isset($params['filterAdminRole'])) { + $query->whereNotIn('id', [env('ADMIN_ROLE')]); + } + + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + return $query; + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemUploadFileMapper.php b/app/System/Mapper/SystemUploadFileMapper.php new file mode 100644 index 0000000..d3afa6c --- /dev/null +++ b/app/System/Mapper/SystemUploadFileMapper.php @@ -0,0 +1,113 @@ +model = SystemUploadfile::class; + } + + /** + * 通过hash获取上传文件的信息 + * @param string $hash + * @return Builder|\Hyperf\Database\Model\Model|object|null + */ + public function getFileInfoByHash(string $hash) + { + return $this->model::query()->where('hash', $hash)->first(); + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['storage_mode'])) { + $query->where('storage_mode', $params['storage_mode']); + } + if (isset($params['origin_name'])) { + $query->where('origin_name', 'like', '%'.$params['origin_name'].'%'); + } + if (isset($params['storage_path'])) { + $query->where('storage_path', 'like', $params['storage_path'].'%'); + } + if (!empty($params['mime_type'])) { + $query->where('mime_type', 'like', $params['mime_type'].'/%'); + } + if (isset($params['minDate']) && isset($params['maxDate'])) { + $query->whereBetween( + 'created_at', + [$params['minDate'] . ' 00:00:00', $params['maxDate'] . ' 23:59:59'] + ); + } + return $query; + } + + public function realDelete(array $ids): bool + { + foreach ($ids as $id) { + $model = $this->model::withTrashed()->find($id); + if ($model) { + $storageMode = match ( $model->storage_mode ) { + 1 => 'local' , + 2 => 'oss' , + 3 => 'qiniu' , + 4 => 'cos' , + default => 'local' , + }; + $event = new \Builder\Event\RealDeleteUploadFile( + $model, $this->container->get(FilesystemFactory::class)->get($storageMode) + ); + $this->evDispatcher->dispatch($event); + if ($event->getConfirm()) { + $model->forceDelete(); + } + } + } + unset($event); + return true; + } + + /** + * 检查数据库中是否存在该目录数据 + * @param string $path + * @return bool + */ + public function checkDirDbExists(string $path): bool + { + return $this->model::withTrashed() + ->where('storage_path', $path) + ->orWhere('storage_path', 'like', $path . '/%') + ->exists(); + } +} \ No newline at end of file diff --git a/app/System/Mapper/SystemUserMapper.php b/app/System/Mapper/SystemUserMapper.php new file mode 100644 index 0000000..f913fd7 --- /dev/null +++ b/app/System/Mapper/SystemUserMapper.php @@ -0,0 +1,238 @@ +model = SystemUser::class; + } + + /** + * 通过用户名检查用户 + * @param string $username + * @return Builder|\Hyperf\Database\Model\Model + */ + public function checkUserByUsername(string $username) + { + return $this->model::query()->where('username', $username)->firstOrFail(); + } + + /** + * 通过用户名检查是否存在 + * @param string $username + * @return bool + */ + public function existsByUsername(string $username): bool + { + return $this->model::query()->where('username', $username)->exists(); + } + + /** + * 检查用户密码 + * @param String $password + * @param string $hash + * @return bool + */ + public function checkPass(String $password, string $hash): bool + { + return $this->model::passwordVerify($password, $hash); + } + + /** + * 新增用户 + * @param array $data + * @return int + */ + #[Transaction] + public function save(array $data): int + { + $role_ids = $data['role_ids'] ?? []; + $post_ids = $data['post_ids'] ?? []; + $dept_ids = $data['dept_ids'] ?? []; + $this->filterExecuteAttributes($data, true); + + $user = $this->model::create($data); + $user->roles()->sync($role_ids, false); + $user->posts()->sync($post_ids, false); + $user->depts()->sync($dept_ids, false); + return $user->id; + } + + /** + * 更新用户 + * @param int $id + * @param array $data + * @return bool + */ + #[Transaction] + public function update(int $id, array $data): bool + { + $role_ids = $data['role_ids'] ?? []; + $post_ids = $data['post_ids'] ?? []; + $dept_ids = $data['dept_ids'] ?? []; + $this->filterExecuteAttributes($data, true); + + $result = parent::update($id, $data); + $user = $this->model::find($id); + if ($user && $result) { + !empty($role_ids) && $user->roles()->sync($role_ids); + !empty($dept_ids) && $user->depts()->sync($dept_ids); + $user->posts()->sync($post_ids); + return true; + } + return false; + } + + /** + * 真实批量删除用户 + * @param array $ids + * @return bool + */ + #[Transaction] + public function realDelete(array $ids): bool + { + foreach ($ids as $id) { + $user = $this->model::withTrashed()->find($id); + if ($user) { + $user->roles()->detach(); + $user->posts()->detach(); + $user->depts()->detach(); + $user->forceDelete(); + } + } + return true; + } + + /** + * 获取用户信息 + * @param int $id + * @return MineModel + */ + public function read(int $id): ?MineModel + { + $user = $this->model::find($id); + if ($user) { + $user->setAttribute('roleList', $user->roles()->get(['id', 'name']) ?: []); + $user->setAttribute('postList', $user->posts()->get(['id', 'name']) ?: []); + $user->setAttribute('deptList', $user->depts()->get(['id', 'name']) ?: []); + } + return $user; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + if (isset($params['dept_id']) && is_string($params['dept_id'])) { + $query->join('system_user_dept as dept', 'system_user.id', '=', 'dept.user_id'); + $query->where('dept.dept_id', '=', $params['dept_id']); + } + if (isset($params['username'])) { + $query->where('username', 'like', '%'.$params['username'].'%'); + } + if (isset($params['nickname'])) { + $query->where('nickname', 'like', '%'.$params['nickname'].'%'); + } + if (isset($params['phone'])) { + $query->where('phone', '=', $params['phone']); + } + if (isset($params['email'])) { + $query->where('email', '=', $params['email']); + } + if (isset($params['status'])) { + $query->where('status', $params['status']); + } + + if (isset($params['filterSuperAdmin'])) { + $query->whereNotIn('id', [env('SUPER_ADMIN')]); + } + + if (isset($params['created_at']) && is_array($params['created_at']) && count($params['created_at']) == 2) { + $query->whereBetween( + 'created_at', + [ $params['created_at'][0] . ' 00:00:00', $params['created_at'][1] . ' 23:59:59' ] + ); + } + + if (isset($params['userIds'])) { + $query->whereIn('id', $params['userIds']); + } + + if (isset($params['showDept'])) { + $isAll = $params['showDeptAll'] ?? false; + + $query->with(['depts' => function($query) use($isAll){ + /* @var Builder $query*/ + $query->where('status', SystemDept::ENABLE); + return $isAll ? $query->select(['*']) : $query->select(['id', 'name']); + }]); + } + + if (isset($params['role_id'])) { + $tablePrefix = env('DB_PREFIX'); + $query->whereRaw( + "id IN ( SELECT user_id FROM {$tablePrefix}system_user_role WHERE role_id = ? )", + [ $params['role_id'] ] + ); + } + + if (isset($params['post_id'])) { + $tablePrefix = env('DB_PREFIX'); + $query->whereRaw( + "id IN ( SELECT user_id FROM {$tablePrefix}system_user_post WHERE post_id = ? )", + [ $params['post_id'] ] + ); + } + + return $query; + } + + /** + * 初始化用户密码 + * @param int $id + * @param string $password + * @return bool + */ + public function initUserPassword(int $id, string $password): bool + { + $model = $this->model::find($id); + if ($model) { + $model->password = $password; + return $model->save(); + } + return false; + } + + /** + * 根据用户ID列表获取用户基础信息 + */ + public function getUserInfoByIds(array $ids, ?array $select = null): array + { + if (! $select) $select = ['id', 'username', 'nickname', 'phone', 'email', 'created_at']; + return $this->model::query()->whereIn('id', $ids)->select($select)->get()->toArray(); + } +} \ No newline at end of file diff --git a/app/System/Middleware/WsAuthMiddleware.php b/app/System/Middleware/WsAuthMiddleware.php new file mode 100644 index 0000000..f5bf5b2 --- /dev/null +++ b/app/System/Middleware/WsAuthMiddleware.php @@ -0,0 +1,38 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace App\System\Middleware; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; + +class WsAuthMiddleware implements MiddlewareInterface +{ + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $token = $request->getQueryParams()['token'] ?? null; + try { + if ($token && user()->check($token)) { + return $handler->handle($request); + } else { + return container()->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->raw(t('jwt.validate_fail')); + } + } catch(\Exception $e) { + return container()->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->raw($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/System/Model/SettingConfig.php b/app/System/Model/SettingConfig.php new file mode 100644 index 0000000..5526cfc --- /dev/null +++ b/app/System/Model/SettingConfig.php @@ -0,0 +1,41 @@ + 'integer', 'sort' => 'integer', 'config_select_data' => 'array']; +} \ No newline at end of file diff --git a/app/System/Model/SettingConfigGroup.php b/app/System/Model/SettingConfigGroup.php new file mode 100644 index 0000000..4547ce7 --- /dev/null +++ b/app/System/Model/SettingConfigGroup.php @@ -0,0 +1,45 @@ + 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 关联config表 + * @return \Hyperf\Database\Model\Relations\HasMany + */ + public function configs() : \Hyperf\Database\Model\Relations\HasMany + { + return $this->hasMany(SettingConfig::class, 'group_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SettingCrontab.php b/app/System/Model/SettingCrontab.php new file mode 100644 index 0000000..0edef28 --- /dev/null +++ b/app/System/Model/SettingCrontab.php @@ -0,0 +1,59 @@ + 'integer', 'type' => 'integer', 'singleton' => 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 关联字典任务日志表 + * @return \Hyperf\Database\Model\Relations\HasMany + */ + public function logs() : \Hyperf\Database\Model\Relations\HasMany + { + return $this->hasMany(SettingCrontabLog::class, 'crontab_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SettingCrontabLog.php b/app/System/Model/SettingCrontabLog.php new file mode 100644 index 0000000..0468a9a --- /dev/null +++ b/app/System/Model/SettingCrontabLog.php @@ -0,0 +1,38 @@ + 'integer', 'crontab_id' => 'integer', 'status' => 'integer']; +} \ No newline at end of file diff --git a/app/System/Model/SettingGenerateColumns.php b/app/System/Model/SettingGenerateColumns.php new file mode 100644 index 0000000..1335891 --- /dev/null +++ b/app/System/Model/SettingGenerateColumns.php @@ -0,0 +1,54 @@ + 'integer', 'table_id' => 'integer', 'is_pk' => 'integer', 'is_required' => 'integer', 'is_insert' => 'integer', 'is_edit' => 'integer', 'is_list' => 'integer', 'is_query' => 'integer', 'is_sort' => 'integer', 'sort' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'options' => 'array']; +} \ No newline at end of file diff --git a/app/System/Model/SettingGenerateTables.php b/app/System/Model/SettingGenerateTables.php new file mode 100644 index 0000000..818bbc6 --- /dev/null +++ b/app/System/Model/SettingGenerateTables.php @@ -0,0 +1,56 @@ + 'integer', 'belong_menu_id' => 'integer', 'generate_type' => 'integer', 'build_menu' => 'integer', 'component_type' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'options' => 'array']; + /** + * 关联生成业务字段信息表 + * @return \Hyperf\Database\Model\Relations\HasMany + */ + public function columns() : \Hyperf\Database\Model\Relations\HasMany + { + return $this->hasMany(SettingGenerateColumns::class, 'table_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemApi.php b/app/System/Model/SystemApi.php new file mode 100644 index 0000000..5d38166 --- /dev/null +++ b/app/System/Model/SystemApi.php @@ -0,0 +1,82 @@ + 'integer', 'group_id' => 'integer', 'auth_mode' => 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 通过中间表关联APP + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function apps() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemApp::class, 'system_app_api', 'api_id', 'app_id'); + } + /** + * 关联API分组 + * @return \Hyperf\Database\Model\Relations\HasOne + */ + public function apiGroup() : \Hyperf\Database\Model\Relations\HasOne + { + return $this->hasOne(SystemApiGroup::class, 'id', 'group_id'); + } + /** + * 关联API字段 + * @return \Hyperf\Database\Model\Relations\hasMany + */ + public function apiColumn() : \Hyperf\Database\Model\Relations\hasMany + { + return $this->hasMany(SystemApiColumn::class, 'api_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemApiColumn.php b/app/System/Model/SystemApiColumn.php new file mode 100644 index 0000000..d9b7bfb --- /dev/null +++ b/app/System/Model/SystemApiColumn.php @@ -0,0 +1,55 @@ + 'integer', 'api_id' => 'integer', 'type' => 'integer', 'is_required' => 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 关联API + * @return \Hyperf\Database\Model\Relations\BelongsTo + */ + public function api() : \Hyperf\Database\Model\Relations\BelongsTo + { + return $this->belongsTo(SystemApi::class, 'api_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemApiGroup.php b/app/System/Model/SystemApiGroup.php new file mode 100644 index 0000000..618fbfb --- /dev/null +++ b/app/System/Model/SystemApiGroup.php @@ -0,0 +1,49 @@ + 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 关联API + * @return \Hyperf\Database\Model\Relations\hasMany + */ + public function apis() : \Hyperf\Database\Model\Relations\hasMany + { + return $this->hasMany(SystemApi::class, 'group_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemApiLog.php b/app/System/Model/SystemApiLog.php new file mode 100644 index 0000000..de785df --- /dev/null +++ b/app/System/Model/SystemApiLog.php @@ -0,0 +1,41 @@ + 'integer', 'api_id' => 'integer', 'request_data' => 'array', 'response_data' => 'array']; +} \ No newline at end of file diff --git a/app/System/Model/SystemApp.php b/app/System/Model/SystemApp.php new file mode 100644 index 0000000..c20f7d0 --- /dev/null +++ b/app/System/Model/SystemApp.php @@ -0,0 +1,62 @@ + 'integer', 'group_id' => 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 通过中间表关联API + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function apis() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemApi::class, 'system_app_api', 'app_id', 'api_id'); + } + /** + * 关联APP分组 + * @return \Hyperf\Database\Model\Relations\HasOne + */ + public function appGroup() : \Hyperf\Database\Model\Relations\HasOne + { + return $this->hasOne(SystemAppGroup::class, 'id', 'group_id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemAppGroup.php b/app/System/Model/SystemAppGroup.php new file mode 100644 index 0000000..5333854 --- /dev/null +++ b/app/System/Model/SystemAppGroup.php @@ -0,0 +1,40 @@ + 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; +} \ No newline at end of file diff --git a/app/System/Model/SystemDept.php b/app/System/Model/SystemDept.php new file mode 100644 index 0000000..1f22704 --- /dev/null +++ b/app/System/Model/SystemDept.php @@ -0,0 +1,69 @@ + 'integer', 'parent_id' => 'integer', 'status' => 'integer', 'sort' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 通过中间表获取角色 + */ + public function roles() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemRole::class, 'system_role_dept', 'dept_id', 'role_id'); + } + /** + * 通过中间表关联部门 + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function users() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemUser::class, 'system_user_dept', 'dept_id', 'user_id'); + } + /** + * 通过中间表关联部门 + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function leader() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemUser::class, 'system_dept_leader', 'dept_id', 'user_id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemDictData.php b/app/System/Model/SystemDictData.php new file mode 100644 index 0000000..41de7a1 --- /dev/null +++ b/app/System/Model/SystemDictData.php @@ -0,0 +1,44 @@ + 'integer', 'type_id' => 'integer', 'sort' => 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; +} \ No newline at end of file diff --git a/app/System/Model/SystemDictType.php b/app/System/Model/SystemDictType.php new file mode 100644 index 0000000..d24accb --- /dev/null +++ b/app/System/Model/SystemDictType.php @@ -0,0 +1,50 @@ + 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 关联字典数据表 + * @return \Hyperf\Database\Model\Relations\HasMany + */ + public function dictData() : \Hyperf\Database\Model\Relations\HasMany + { + return $this->hasMany(SystemDictData::class, 'type_id', 'id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemLoginLog.php b/app/System/Model/SystemLoginLog.php new file mode 100644 index 0000000..9755bf6 --- /dev/null +++ b/app/System/Model/SystemLoginLog.php @@ -0,0 +1,42 @@ + 'integer', 'status' => 'integer']; +} \ No newline at end of file diff --git a/app/System/Model/SystemMenu.php b/app/System/Model/SystemMenu.php new file mode 100644 index 0000000..84b182d --- /dev/null +++ b/app/System/Model/SystemMenu.php @@ -0,0 +1,65 @@ + 'integer', 'parent_id' => 'integer', 'is_hidden' => 'integer', 'status' => 'integer', 'sort' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 通过中间表获取角色 + */ + public function roles() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemRole::class, 'system_role_menu', 'menu_id', 'role_id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemNotice.php b/app/System/Model/SystemNotice.php new file mode 100644 index 0000000..404a4b9 --- /dev/null +++ b/app/System/Model/SystemNotice.php @@ -0,0 +1,43 @@ + 'integer', 'message_id' => 'integer', 'type' => 'integer', 'click_num' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; +} \ No newline at end of file diff --git a/app/System/Model/SystemOperLog.php b/app/System/Model/SystemOperLog.php new file mode 100644 index 0000000..71c5a71 --- /dev/null +++ b/app/System/Model/SystemOperLog.php @@ -0,0 +1,47 @@ + 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; +} \ No newline at end of file diff --git a/app/System/Model/SystemPost.php b/app/System/Model/SystemPost.php new file mode 100644 index 0000000..c6e89f4 --- /dev/null +++ b/app/System/Model/SystemPost.php @@ -0,0 +1,50 @@ + 'integer', 'sort' => 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 通过中间表获取用户 + */ + public function users() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemUser::class, 'system_user_post', 'post_id', 'user_id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemQueueLog.php b/app/System/Model/SystemQueueLog.php new file mode 100644 index 0000000..4f36c18 --- /dev/null +++ b/app/System/Model/SystemQueueLog.php @@ -0,0 +1,81 @@ + 'integer', 'produce_status' => 'integer', 'consume_status' => 'integer', 'delay_time' => 'integer', 'created_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; +} \ No newline at end of file diff --git a/app/System/Model/SystemQueueMessage.php b/app/System/Model/SystemQueueMessage.php new file mode 100644 index 0000000..8c0a546 --- /dev/null +++ b/app/System/Model/SystemQueueMessage.php @@ -0,0 +1,83 @@ + 'integer', 'content_id' => 'integer', 'send_by' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 关联发送人 + * @return \Hyperf\Database\Model\Relations\HasOne + */ + public function sendUser() : \Hyperf\Database\Model\Relations\HasOne + { + return $this->hasOne(SystemUser::class, 'id', 'send_by'); + } + /** + * 关联接收人中间表 + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function receiveUser() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemUser::class, 'system_queue_message_receive', 'message_id', 'user_id')->as('receive_users')->withPivot(...['read_status']); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemRole.php b/app/System/Model/SystemRole.php new file mode 100644 index 0000000..9fa8ebc --- /dev/null +++ b/app/System/Model/SystemRole.php @@ -0,0 +1,77 @@ + 'integer', 'data_scope' => 'integer', 'status' => 'integer', 'sort' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; + /** + * 通过中间表获取菜单 + */ + public function menus() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemMenu::class, 'system_role_menu', 'role_id', 'menu_id'); + } + /** + * 通过中间表获取用户 + */ + public function users() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemUser::class, 'system_user_role', 'role_id', 'user_id'); + } + /** + * 通过中间表获取部门 + */ + public function depts() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemDept::class, 'system_role_dept', 'role_id', 'dept_id'); + } +} \ No newline at end of file diff --git a/app/System/Model/SystemUploadfile.php b/app/System/Model/SystemUploadfile.php new file mode 100644 index 0000000..e45ecf2 --- /dev/null +++ b/app/System/Model/SystemUploadfile.php @@ -0,0 +1,48 @@ + 'integer', 'storage_mode' => 'integer', 'size_byte' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; +} \ No newline at end of file diff --git a/app/System/Model/SystemUser.php b/app/System/Model/SystemUser.php new file mode 100644 index 0000000..ca3923d --- /dev/null +++ b/app/System/Model/SystemUser.php @@ -0,0 +1,103 @@ + 'integer', 'status' => 'integer', 'created_by' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'backend_setting' => 'array']; + /** + * 通过中间表关联角色 + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function roles() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemRole::class, 'system_user_role', 'user_id', 'role_id'); + } + /** + * 通过中间表关联岗位 + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function posts() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemPost::class, 'system_user_post', 'user_id', 'post_id'); + } + /** + * 通过中间表关联部门 + * @return \Hyperf\Database\Model\Relations\BelongsToMany + */ + public function depts() : \Hyperf\Database\Model\Relations\BelongsToMany + { + return $this->belongsToMany(SystemDept::class, 'system_user_dept', 'user_id', 'dept_id'); + } + /** + * 密码加密 + * @param $value + * @return void + */ + public function setPasswordAttribute($value) : void + { + $this->attributes['password'] = password_hash($value, PASSWORD_DEFAULT); + } + /** + * 验证密码 + * @param $password + * @param $hash + * @return bool + */ + public static function passwordVerify($password, $hash) : bool + { + return password_verify($password, $hash); + } +} \ No newline at end of file diff --git a/app/System/Queue/Consumer/MessageConsumer.php b/app/System/Queue/Consumer/MessageConsumer.php new file mode 100644 index 0000000..29bab45 --- /dev/null +++ b/app/System/Queue/Consumer/MessageConsumer.php @@ -0,0 +1,41 @@ +info( + sprintf( + 'MineAdmin created queue message time at: %s, data is: %s', + date('Y-m-d H:i:s'), + (is_array($data) || is_object($data)) ? json_encode($data) : $data + ) + ); + + $this->payload = $data; + } +} diff --git a/app/System/Request/GenerateRequest.php b/app/System/Request/GenerateRequest.php new file mode 100644 index 0000000..be7babc --- /dev/null +++ b/app/System/Request/GenerateRequest.php @@ -0,0 +1,69 @@ + 'required', + 'generate_type' => 'required', + 'build_menu' => 'required', + 'generate_menus' => 'array', + 'menu_name' => 'required', + 'module_name' => 'required', + 'table_comment' => 'required', + 'table_name' => 'required', + 'type' => 'required', + 'component_type' => 'required', + 'columns' => 'required|array', + 'package_name' => '', + 'belong_menu_id' => '', + 'options' => '', + 'remark' => '', + ]; + } + + public function loadTableRules(): array + { + return [ + 'names' => 'required|array', + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '业务表ID', + 'generate_type' => '生成类型', + 'build_menu' => '是否构建菜单', + 'generate_menus' => '生成菜单列表', + 'menu_name' => '菜单名称', + 'module_name' => '模块名称', + 'table_comment' => '业务表说明', + 'table_name' => '业务表名称', + 'type' => '生成类型', + 'component_type' => '组件类型', + 'columns' => '字段列表', + 'names' => '业务表名称组' + ]; + } +} \ No newline at end of file diff --git a/app/System/Request/MessageRequest.php b/app/System/Request/MessageRequest.php new file mode 100644 index 0000000..2c155d9 --- /dev/null +++ b/app/System/Request/MessageRequest.php @@ -0,0 +1,41 @@ + 'required', + 'users' => 'required|array', + 'content' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'title' => '信息标题', + 'users' => '接受用户列表', + 'content' => '信息内容', + ]; + } +} \ No newline at end of file diff --git a/app/System/Request/ModuleRequest.php b/app/System/Request/ModuleRequest.php new file mode 100644 index 0000000..ff3bb90 --- /dev/null +++ b/app/System/Request/ModuleRequest.php @@ -0,0 +1,59 @@ + 'required|regex:/^[A-Za-z]{2,}$/i', + 'label' => 'required', + 'version' => 'required|regex:/^[0-9\.]{3,}$/', + 'description' => 'required|max:255', + ]; + } + /** + * 修改状态数据验证规则 + * return array + */ + public function modifyStatusRules(): array + { + return [ + 'name' => 'required', + 'status' => 'required', + ]; + } + + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'name' => '模块名称', + 'label' => '模块标识', + 'version' => '模块版本号', + 'description' => '模块描述', + 'status' => '模块状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SettingConfigGroupRequest.php b/app/System/Request/SettingConfigGroupRequest.php new file mode 100644 index 0000000..9081b4a --- /dev/null +++ b/app/System/Request/SettingConfigGroupRequest.php @@ -0,0 +1,51 @@ + 'required|max:32', + 'code' => 'required|max:64', + ]; + } + + /** + * Get the validation rules that apply to the request. + */ + public function saveRules(): array + { + return []; + } + + /** + * Get the validation rules that apply to the request. + */ + public function updateRules(): array + { + return [ + 'id' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '主键', + 'name' => '配置组名称', + 'code' => '配置组标识', + ]; + } +} \ No newline at end of file diff --git a/app/System/Request/SettingConfigRequest.php b/app/System/Request/SettingConfigRequest.php new file mode 100644 index 0000000..6522bde --- /dev/null +++ b/app/System/Request/SettingConfigRequest.php @@ -0,0 +1,58 @@ + 'required', + 'key' => 'required|max:32', + 'value' => 'required|max:255', + 'name' => 'required|max:255', + 'input_type' => '', + 'config_select_data' => '', + 'sort' => '', + 'remark' => '', + ]; + } + + /** + * Get the validation rules that apply to the request. + */ + public function saveRules(): array + { + return [ + ]; + } + + /** + * Get the validation rules that apply to the request. + */ + public function updateRules(): array + { + return [ + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'group_id' => '组ID', + 'key' => '配置键名', + 'value' => '配置值', + 'name' => '配置名称', + ]; + } +} \ No newline at end of file diff --git a/app/System/Request/SettingCrontabRequest.php b/app/System/Request/SettingCrontabRequest.php new file mode 100644 index 0000000..28ccdf1 --- /dev/null +++ b/app/System/Request/SettingCrontabRequest.php @@ -0,0 +1,54 @@ + 'required', + 'type' => 'required', + 'rule' => 'required', + 'target' => 'required', + ]; + } + + /** + * 新增数据验证规则 + * return array + */ + public function saveRules(): array + { + return []; + } + + /** + * 新增数据验证规则 + * return array + */ + public function updateRules(): array + { + return []; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'name' => '任务名称', + 'type' => '任务类型', + 'target' => '调用目标', + 'rule' => '定时规则表达式', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemApiColumnRequest.php b/app/System/Request/SystemApiColumnRequest.php new file mode 100644 index 0000000..de2458c --- /dev/null +++ b/app/System/Request/SystemApiColumnRequest.php @@ -0,0 +1,76 @@ + 'required', + 'api_id' => 'required', + 'type' => 'required', + 'data_type' => 'required', + 'is_required' => 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required', + 'type' => 'required', + 'data_type' => 'required', + 'is_required' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '接口ID', + 'name' => '字段名称', + 'api_id' => '接口ID', + 'type' => '字段类型', + 'data_type' => '数据类型', + 'is_required' => '是否必填', + 'status' => '接口状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemApiGroupRequest.php b/app/System/Request/SystemApiGroupRequest.php new file mode 100644 index 0000000..dc482a2 --- /dev/null +++ b/app/System/Request/SystemApiGroupRequest.php @@ -0,0 +1,64 @@ + 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '组ID', + 'name' => '组名称', + 'status' => '组状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemApiRequest.php b/app/System/Request/SystemApiRequest.php new file mode 100644 index 0000000..7c7733e --- /dev/null +++ b/app/System/Request/SystemApiRequest.php @@ -0,0 +1,77 @@ + 'required', + 'class_name' => 'required', + 'method_name' => 'required', + 'auth_mode' => 'required', + 'request_mode' => 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required', + 'class_name' => 'required', + 'method_name' => 'required', + 'auth_mode' => 'required', + 'request_mode' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '接口ID', + 'name' => '接口名称', + 'class_name' => '类命名空间地址', + 'method_name' => '方法名', + 'auth_mode' => '验证模式', + 'request_mode' => '请求方式', + 'status' => '接口状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemAppGroupRequest.php b/app/System/Request/SystemAppGroupRequest.php new file mode 100644 index 0000000..1befe74 --- /dev/null +++ b/app/System/Request/SystemAppGroupRequest.php @@ -0,0 +1,64 @@ + 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '组ID', + 'name' => '组名称', + 'status' => '组状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemAppRequest.php b/app/System/Request/SystemAppRequest.php new file mode 100644 index 0000000..da3eb6e --- /dev/null +++ b/app/System/Request/SystemAppRequest.php @@ -0,0 +1,74 @@ + 'required', + 'app_name' => 'required', + 'app_id' => 'required', + 'app_secret' => 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'group_id' => 'required', + 'app_name' => 'required', + 'app_id' => 'required', + 'app_secret' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '应用ID', + 'group_id' => '应用分组', + 'app_name' => '应用名称', + 'app_id' => 'APP ID', + 'app_secret' => 'APP SECRET', + 'status' => '应用状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemDeptRequest.php b/app/System/Request/SystemDeptRequest.php new file mode 100644 index 0000000..255066b --- /dev/null +++ b/app/System/Request/SystemDeptRequest.php @@ -0,0 +1,78 @@ + 'required|max:30' + ]; + } + + /** + * 新增部门领导验证规则 + * return array + */ + public function addLeaderRules(): array + { + return [ + 'id' => 'required', + 'users' => 'required' + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required|max:30' + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '部门ID', + 'name' => '部门名称', + 'status' => '部门状态', + 'users' => '用户信息', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemDictDataRequest.php b/app/System/Request/SystemDictDataRequest.php new file mode 100644 index 0000000..b7667bf --- /dev/null +++ b/app/System/Request/SystemDictDataRequest.php @@ -0,0 +1,71 @@ + 'required', + 'code' => 'required', + 'value' => 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'label' => 'required', + 'code' => 'required', + 'value' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '字典ID', + 'name' => '字典名称', + 'code' => '字典标识', + 'value' => '字典', + 'status' => '字典状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemDictTypeRequest.php b/app/System/Request/SystemDictTypeRequest.php new file mode 100644 index 0000000..a225052 --- /dev/null +++ b/app/System/Request/SystemDictTypeRequest.php @@ -0,0 +1,68 @@ + 'required', + 'code' => 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required', + 'code' => 'required', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '字典类型ID', + 'name' => '字典类型名称', + 'code' => '字典类型标识', + 'status' => '字典类型状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemMenuRequest.php b/app/System/Request/SystemMenuRequest.php new file mode 100644 index 0000000..f9f4768 --- /dev/null +++ b/app/System/Request/SystemMenuRequest.php @@ -0,0 +1,67 @@ + 'required|max:30', + 'code' => 'required|min:3|max:50', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required|max:30', + 'code' => 'required|min:3|max:50', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '菜单ID', + 'name' => '菜单名称', + 'code' => '菜单标识', + 'status' => '菜单状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemNoticeRequest.php b/app/System/Request/SystemNoticeRequest.php new file mode 100644 index 0000000..3c3f63f --- /dev/null +++ b/app/System/Request/SystemNoticeRequest.php @@ -0,0 +1,56 @@ + 'required', + 'type' => 'required', + 'content' => 'required', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'title' => 'required', + 'type' => 'required', + 'content' => 'required', + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'title' => '公告标题', + 'type' => '公告类型', + 'content' => '公告内容', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemPostRequest.php b/app/System/Request/SystemPostRequest.php new file mode 100644 index 0000000..24f3973 --- /dev/null +++ b/app/System/Request/SystemPostRequest.php @@ -0,0 +1,68 @@ + 'required|max:30', + 'code' => 'required|min:3|max:100', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required|max:30', + 'code' => 'required|min:3|max:100', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '岗位ID', + 'name' => '岗位名称', + 'code' => '岗位标识', + 'status' => '岗位状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemRoleRequest.php b/app/System/Request/SystemRoleRequest.php new file mode 100644 index 0000000..62552c6 --- /dev/null +++ b/app/System/Request/SystemRoleRequest.php @@ -0,0 +1,68 @@ + 'required|max:30', + 'code' => 'required|min:3|max:100', + ]; + } + + /** + * 更新数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'name' => 'required|max:30', + 'code' => 'required|min:3|max:100', + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '角色ID', + 'name' => '角色名称', + 'code' => '角色标识', + 'status' => '角色状态', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/SystemUserRequest.php b/app/System/Request/SystemUserRequest.php new file mode 100644 index 0000000..660b66d --- /dev/null +++ b/app/System/Request/SystemUserRequest.php @@ -0,0 +1,121 @@ + 'required|max:20', + 'password' => 'required|min:6', + 'dept_ids' => 'required', + 'role_ids' => 'required' + ]; + } + + /** + * 新增数据验证规则 + * return array + */ + public function updateRules(): array + { + return [ + 'username' => 'required|max:20', + 'dept_ids' => 'required', + 'role_ids' => 'required' + ]; + } + + /** + * 修改状态数据验证规则 + * return array + */ + public function changeStatusRules(): array + { + return [ + 'id' => 'required', + 'status' => 'required' + ]; + } + + /** + * 修改密码验证规则 + * return array + */ + public function modifyPasswordRules(): array + { + return [ + 'newPassword' => 'required|confirmed', + 'newPassword_confirmation' => 'required', + 'oldPassword' => ['required', function ($attribute, $value, $fail) { + $service = $this->container->get(SystemUserService::class); + /* @var SystemUser $model */ + $model = $service->mapper->getModel()::find((int) user()->getId(), ['password']); + if (! $service->mapper->checkPass($value, $model->password)) { + $fail(t('system.valid_password')); + } + }], + ]; + } + + /** + * 设置用户首页数据验证规则 + */ + public function setHomePageRules(): array + { + return [ + 'id' => 'required', + 'dashboard' => 'required' + ]; + } + + /** + * 登录规则 + * @return string[] + */ + public function loginRules(): array + { + return [ + 'username' => 'required|max:20', + 'password' => 'required|min:6', + ]; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'id' => '用户ID', + 'username' => '用户名', + 'password' => '用户密码', + 'dashboard' => '用户后台首页', + 'oldPassword' => '旧密码', + 'newPassword' => '新密码', + 'newPassword_confirmation' => '确认密码', + 'status' => '用户状态', + 'dept_ids' => '部门ID', + 'role_ids' => '角色列表', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Request/UploadRequest.php b/app/System/Request/UploadRequest.php new file mode 100644 index 0000000..4cb9a10 --- /dev/null +++ b/app/System/Request/UploadRequest.php @@ -0,0 +1,115 @@ + 'required|mimes:' . $this->getMimes('upload_allow_file'), + 'path' => 'max:30', + ]; + } + + /** + * 上传图片验证规则 + * @return string[] + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \RedisException + */ + public function uploadImageRules(): array + { + return [ + 'image' => 'required|mimes:' . $this->getMimes('upload_allow_image'), + 'path' => 'max:30', + ]; + } + + /** + * 分块上传验证规则 + * @return string[] + */ + public function chunkUploadRules(): array + { + return [ + 'package' => 'required', + 'total' => 'required', + 'index' => 'required', + 'hash' => 'required', + 'ext' => 'required', + 'type' => 'required', + 'name' => 'required', + 'size' => 'required', + ]; + } + + /** + * 分块上传验证规则 + * @return string[] + */ + public function saveNetworkImageRules(): array + { + return [ + 'url' => 'required', + 'path' => 'max:30', + ]; + } + + /** + * 获取Mimes + * @param $key + * @return string + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \RedisException + */ + protected function getMimes($key): string + { + return container()->get(SettingConfigService::class)->getConfigByKey($key)['value'] ?? ''; + } + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ + 'url' => '网络图片地址', + 'path' => '保存目录', + 'image' => '上传图片', + 'file' => '上传文件', + 'package' => '文件数据包', + 'total' => '总分块数', + 'index' => '分块索引', + 'hash' => '文件hash', + 'ext' => '文件扩展名', + 'type' => '文件类型', + 'name' => '文件名称', + 'size' => '文件大小', + ]; + } + +} \ No newline at end of file diff --git a/app/System/Service/CacheMonitorService.php b/app/System/Service/CacheMonitorService.php new file mode 100644 index 0000000..907c4c3 --- /dev/null +++ b/app/System/Service/CacheMonitorService.php @@ -0,0 +1,79 @@ +get(Redis::class); + + $info = $redis->info(); + + $iterator = null; + $keys = []; + while (false !== ($key = $redis->scan($iterator, config('cache.default.prefix').'*', 100))) { + $keys = array_merge($keys, $key); + } + + return [ + 'keys' => &$keys, + 'server' => [ + 'version' => &$info['redis_version'], + 'redis_mode' => ($info['redis_mode'] === 'standalone') ? '单机' : '集群', + 'run_days' => &$info['uptime_in_days'], + 'aof_enabled' => ($info['aof_enabled'] == 0) ? '关闭' : '开启', + 'use_memory' => &$info['used_memory_human'], + 'port' => &$info['tcp_port'], + 'clients' => &$info['connected_clients'], + 'expired_keys' => &$info['expired_keys'], + 'sys_total_keys' => count($keys) + ] + ]; + } + + /** + * 查看缓存内容 + * @param string $key + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function view(string $key): string + { + return container()->get(Redis::class)->get($key); + } + + /** + * 删除一个缓存 + * @param string $key + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function delete(string $key): bool + { + return container()->get(Redis::class)->del($key) > 0; + } + + /** + * 清空所有缓存 + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function clear(): bool + { + return container()->get(Redis::class)->flushDB(); + } + +} \ No newline at end of file diff --git a/app/System/Service/DataMaintainService.php b/app/System/Service/DataMaintainService.php new file mode 100644 index 0000000..194dcb7 --- /dev/null +++ b/app/System/Service/DataMaintainService.php @@ -0,0 +1,126 @@ +getArrayToPageList($params); + } + + /** + * 数组数据搜索器 + * @param \Hyperf\Utils\Collection $collect + * @param array $params + * @return Collection + */ + protected function handleArraySearch(\Hyperf\Utils\Collection $collect, array $params): \Hyperf\Utils\Collection + { + if ($params['name'] ?? false) { + $collect = $collect->filter(function ($row) use ($params) { + return \Builder\Helper\Str::contains($row->Name, $params['name']); + }); + } + if ($params['engine'] ?? false) { + $collect = $collect->where('Engine', $params['engine']); + } + return $collect; + } + + /** + * 数组当前页数据返回之前处理器,默认对key重置 + * @param array $data + * @param array $params + * @return array + */ + protected function getCurrentArrayPageBefore(array &$data, array $params = []): array + { + $tables = []; + foreach ($data as $item) { + $tables[] = array_change_key_case((array)$item); + } + return $tables; + } + + /** + * 设置需要分页的数组数据 + * @param array $params + * @return array + */ + protected function getArrayData(array $params = []): array + { + return Db::select(Db::raw("SHOW TABLE STATUS WHERE name NOT LIKE '%migrations'")->getValue()); + } + + /** + * 获取表字段 + * @param string $table + * @return array + */ + public function getColumnList(string $table): array + { + if ($table) { + //从数据库中获取表字段信息 + $sql = "SELECT * FROM `information_schema`.`columns` " + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $columnList = []; + foreach (Db::select($sql, [env('DB_DATABASE'), $table]) as $column) { + $columnList[] = [ + 'column_key' => $column->COLUMN_KEY, + 'column_name'=> $column->COLUMN_NAME, + 'data_type' => $column->DATA_TYPE, + 'column_comment' => $column->COLUMN_COMMENT, + 'extra' => $column->EXTRA, + 'column_type' => $column->COLUMN_TYPE, + 'is_nullable' => $column->IS_NULLABLE, + ]; + } + return $columnList; + } else { + return []; + } + } + + /** + * 优化表 + * @param array $tables + * @return bool + */ + public function optimize(array $tables): bool + { + foreach ($tables as $table) { + Db::select('optimize table `?`', [$table]); + } + return true; + } + + /** + * 清理表碎片 + * @param array $tables + * @return bool + */ + public function fragment(array $tables): bool + { + foreach ($tables as $table) { + Db::select('analyze table `?`', [$table]); + } + return true; + } + + +} \ No newline at end of file diff --git a/app/System/Service/Dependencies/UserAuthService.php b/app/System/Service/Dependencies/UserAuthService.php new file mode 100644 index 0000000..708751d --- /dev/null +++ b/app/System/Service/Dependencies/UserAuthService.php @@ -0,0 +1,100 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ +namespace App\System\Service\Dependencies; + +use App\System\Mapper\SystemUserMapper; +use App\System\Model\SystemUser; +use Hyperf\Database\Model\ModelNotFoundException; +use Builder\Event\UserLoginAfter; +use Builder\Event\UserLoginBefore; +use Builder\Event\UserLogout; +use Builder\Exception\NormalStatusException; +use Builder\Exception\UserBanException; +use Builder\Helper\MineCode; +use Builder\Interfaces\UserServiceInterface; +use Builder\Vo\UserServiceVo; + +/** + * 用户登录 + */ +class UserAuthService implements UserServiceInterface +{ + + /** + * 登录 + * @param UserServiceVo $userServiceVo + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function login(UserServiceVo $userServiceVo): string + { + $mapper = container()->get(SystemUserMapper::class); + try { + event(new UserLoginBefore(['username' => $userServiceVo->getUsername(), 'password' => $userServiceVo->getPassword()])); + $userinfo = $mapper->checkUserByUsername($userServiceVo->getUsername())->toArray(); + $password = $userinfo['password']; + unset($userinfo['password']); + $userLoginAfter = new UserLoginAfter($userinfo); + if ($mapper->checkPass($userServiceVo->getPassword(), $password)) { + if ( + ($userinfo['status'] == SystemUser::USER_NORMAL) + || + ($userinfo['status'] == SystemUser::USER_BAN && $userinfo['id'] == env('SUPER_ADMIN')) + ) { + $userLoginAfter->message = t('jwt.login_success'); + $token = user()->getToken($userLoginAfter->userinfo); + $userLoginAfter->token = $token; + event($userLoginAfter); + return $token; + } else { + $userLoginAfter->loginStatus = false; + $userLoginAfter->message = t('jwt.user_ban'); + event($userLoginAfter); + throw new UserBanException; + } + } else { + $userLoginAfter->loginStatus = false; + $userLoginAfter->message = t('jwt.password_error'); + event($userLoginAfter); + throw new NormalStatusException; + } + } catch (\Exception $e) { + if ($e instanceof ModelNotFoundException) { + throw new NormalStatusException(t('jwt.username_error'), MineCode::NO_DATA); + } + if ($e instanceof NormalStatusException) { + throw new NormalStatusException(t('jwt.password_error'), MineCode::PASSWORD_ERROR); + } + if ($e instanceof UserBanException) { + throw new NormalStatusException(t('jwt.user_ban'), MineCode::USER_BAN); + } + console()->error($e->getMessage()); + throw new NormalStatusException(t('jwt.unknown_error')); + } + } + + /** + * 用户退出 + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function logout() + { + $user = user(); + event(new UserLogout($user->getUserInfo())); + $user->getJwt()->logout(); + } +} \ No newline at end of file diff --git a/app/System/Service/ModuleService.php b/app/System/Service/ModuleService.php new file mode 100644 index 0000000..422ce84 --- /dev/null +++ b/app/System/Service/ModuleService.php @@ -0,0 +1,218 @@ +mine = $mine; + } + + /** + * 获取表状态分页列表 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getPageList(?array $params = [], bool $isScope = true): array + { + return $this->getArrayToPageList($params); + } + + /** + * 数组数据搜索器 + * @param Collection $collect + * @param array $params + * @return Collection + */ + protected function handleArraySearch(Collection $collect, array $params): Collection + { + if ($params['name'] ?? false) { + $collect = $collect->filter(function ($row) use ($params) { + return \Builder\Helper\Str::contains($row['name'], $params['name']); + }); + } + + if ($params['label'] ?? false) { + $collect = $collect->filter(function ($row) use ($params) { + return \Builder\Helper\Str::contains($row['label'], $params['label']); + }); + } + return $collect; + } + + /** + * 设置需要分页的数组数据 + * @param array $params + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function getArrayData(array $params = []): array + { + return $this->getModuleCache(); + } + + /** + * 创建模块 + * @param array $moduleInfo + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function createModule(array $moduleInfo): bool + { + /** @var ModuleGenerator $moduleGen */ + $moduleGen = make(ModuleGenerator::class); + $moduleGen->setModuleInfo($moduleInfo)->createModule(); + $this->setModuleCache(); + return true; + } + + /** + * 执行模块安装 + * @param string $name + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function installModuleData(string $name): bool + { + try { + $migrateCommand = [ 'command' => 'mine:migrate-run', 'name' => $name ]; + $seedCommand = [ 'command' => 'mine:seeder-run', 'name' => $name ]; + $application = container()->get(\Hyperf\Contract\ApplicationInterface::class); + $application->setAutoExit(false); + $application->run(new ArrayInput($migrateCommand), new NullOutput()); + $application->run(new ArrayInput($seedCommand), new NullOutput()); + $this->setModuleCache(); + return true; + } catch (\Throwable $e) { + console()->error($e->getMessage()); + return false; + } + } + + /** + * 卸载模块 + * @param string $name + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + public function uninstallModule(string $name): bool + { + try { + $migrate = container()->get(\Hyperf\Database\Migrations\Migrator::class); + $path = BASE_PATH . '/app/' . $name . '/Database/Migrations'; + $migrate->rollback([$path]); + is_dir($path . '/Update') && $migrate->rollback([$path . '/Update']); + $this->deleteModule($name); + $this->setModuleCache(); + return true; + } catch (\Throwable $e) { + console()->error($e->getMessage()); + return false; + } + } + + /** + * 删除模块 + * @param string $name + * @return bool + */ + public function deleteModule(string $name): bool + { + /** @var Filesystem $filesystem */ + $filesystem = make(Filesystem::class); + $modulePath = BASE_PATH . '/app/' . ucfirst($name); + return $filesystem->deleteDirectory($modulePath); + } + + /** + * 缓存模块信息 + * @param string|null $moduleName 模块名 + * @param array $data 模块数据 + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setModuleCache(?string $moduleName = null, array $data = []): void + { + $key = $this->prefix . 'modules'; + $this->mine->scanModule(); + $modules = $this->mine->getModuleInfo(); + if (! empty($moduleName)) { + $modules[$moduleName] = $data; + } + redis()->set($key, serialize($modules)); + } + + /** + * 获取模块缓存信息 + * @param string|null $moduleName + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getModuleCache(?string $moduleName = null): array + { + $key = $this->prefix . 'modules'; + $redis = redis(); + if ($data = $redis->get($key)) { + $data = unserialize($data); + return !empty($moduleName) && isset($data[$moduleName]) ? $data[$moduleName] : $data; + } else { + $this->setModuleCache(); + $this->mine->scanModule(); + return $this->mine->getModuleInfo(); + } + } + + /** + * 启停用模块 + * @param array $data + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function modifyStatus(array $data): bool + { + $modules = make(Mine::class)->getModuleInfo(); + if (isset($modules[$data['name']])) { + $filePath = BASE_PATH . '/app/' . $data['name'] . '/config.json'; + $status = $data['status'] ? 'true' : 'false'; + $content = preg_replace( + '/\"enabled\":\s(true|false),/', + '"enabled": ' . $status . ',', + file_get_contents($filePath) + ); + $result = (bool) file_put_contents($filePath, $content); + $this->setModuleCache(); + return $result; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/app/System/Service/ServerMonitorService.php b/app/System/Service/ServerMonitorService.php new file mode 100644 index 0000000..2e3ea22 --- /dev/null +++ b/app/System/Service/ServerMonitorService.php @@ -0,0 +1,195 @@ +getCpuUsage(); + preg_match('/(\d+)/', shell_exec('cat /proc/cpuinfo | grep "cache size"'), $cache); + } else { + preg_match('/(\d+\.\d+)%\suser/', shell_exec('top -l 1 | head -n 10 | grep CPU'), $cpu); + $cpu = $cpu[1] ?? '未知'; + preg_match('/(\d+)/', shell_exec('system_profiler SPHardwareDataType | grep L2'), $cache); + $cache = $cache[1] ?? '未知'; + } + return [ + 'name' => $this->getCpuName(), + 'cores' => '物理核心数:' . $this->getCpuPhysicsCores() . '个,逻辑核心数:' . $this->getCpuLogicCores() . '个', + 'cache' => $cache[1] ? $cache[1] / 1024 : 0, + 'usage' => $cpu, + 'free' => 100 - $cpu + ]; + } catch (\Throwable $e) { + $res = '无法获取'; + return [ + 'name' => $res, 'cores' => $res, 'cache' => $res, 'usage' => $res, 'free' => $res, + ]; + } + } + + /** + * 获取CPU名称 + * @return string + */ + public function getCpuName(): string + { + if (PHP_OS == 'Linux') { + preg_match('/^\s+\d\s+(.+)/', shell_exec('cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c'), $matches); + return $matches[1] ?? "未知"; + } else { + return shell_exec('sysctl -n machdep.cpu.brand_string'); + } + } + + /** + * 获取cpu物理核心数 + */ + public function getCpuPhysicsCores(): string + { + if (PHP_OS == 'Linux') { + return str_replace("\n", '', shell_exec('cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l')); + } else { + return shell_exec('sysctl hw.physicalcpu'); + } + } + + /** + * 获取cpu逻辑核心数 + */ + public function getCpuLogicCores(): string + { + if (PHP_OS == 'Linux') { + return str_replace("\n", '', shell_exec('cat /proc/cpuinfo |grep "processor"|wc -l')); + } else { + return shell_exec('sysctl hw.logicalcpu'); + } + } + + /** + * 获取CPU使用率 + * @return string + */ + public function getCpuUsage(): string + { + $start = $this->calculationCpu(); + sleep(1); + $end = $this->calculationCpu(); + + $totalStart = $start['total']; + $totalEnd = $end['total']; + + $timeStart = $start['time']; + $timeEnd = $end['time']; + + return sprintf('%.2f', ($timeEnd - $timeStart) / ($totalEnd - $totalStart) * 100); + } + + /** + * 计算CPU + * @return array + */ + protected function calculationCpu(): array + { + $mode = '/(cpu)[\s]+([0-9]+)[\s]+([0-9]+)[\s]+([0-9]+)[\s]+([0-9]+)[\s]+([0-9]+)[\s]+([0-9]+)[\s]+([0-9]+)[\s]+([0-9]+)/'; + $string = shell_exec('more /proc/stat | grep cpu'); + preg_match_all($mode, $string,$matches); + + $total = $matches[2][0] + $matches[3][0] + $matches[4][0] + $matches[5][0] + $matches[6][0] + $matches[7][0] + $matches[8][0] + $matches[9][0]; + $time = $matches[2][0] + $matches[3][0] + $matches[4][0] + $matches[6][0] + $matches[7][0] + $matches[8][0] + $matches[9][0]; + + return ['total' => $total, 'time' => $time]; + } + + /** + * 获取内存信息 + * @return array + */ + public function getMemInfo(): array + { + if (PHP_OS == 'Linux') { + $string = shell_exec('cat /proc/meminfo | grep MemTotal'); + preg_match('/(\d+)/', $string, $total); + $result['total'] = sprintf('%.2f', $total[1] / 1024 / 1024); + + $string = shell_exec('cat /proc/meminfo | grep MemAvailable'); + preg_match('/(\d+)/', $string, $available); + + $result['free'] = sprintf('%.2f', $available[1] / 1024 / 1024); + + $result['usage'] = sprintf('%.2f', ($total[1] - $available[1]) / 1024 / 1024); + + $result['php'] = round(memory_get_usage() / 1024 / 1024, 2); + + $result['rate'] = sprintf( + '%.2f', (sprintf('%.2f', $result['usage']) / sprintf('%.2f', $result['total'])) * 100 + ); + } else { + preg_match('/(\d+)/', shell_exec('system_profiler SPHardwareDataType | grep Memory'), $total); + $result['total'] = $total[1]; + preg_match('/(\d+)[G|M]\sused/', shell_exec('system_profiler SPHardwareDataType | grep Memory'), $usage); + $result['usage'] = $usage[1]; + $result['free'] = $result['total'] - $result['usage']; + $result['php'] = round(memory_get_usage() / 1024 / 1024, 2); + $result['rate'] = sprintf( + '%.2f', (sprintf('%.2f', $result['usage']) / sprintf('%.2f', $result['total'])) * 100 + ); + } + + return $result; + } + + /** + * 获取PHP及环境信息 + * @return array + */ + public function getPhpAndEnvInfo(): array + { + preg_match('/(\d\.\d\.\d)/', shell_exec('php --ri swoole | grep Version'), $matches); + $result['swoole_version'] = $matches[1]; + + $result['php_version'] = PHP_VERSION; + + $result['os'] = PHP_OS; + + $result['project_path'] = BASE_PATH; + + $result['start_time'] = date('Y-m-d H:i:s', START_TIME); + + $result['run_time'] = \Builder\Helper\Str::Sec2Time(time() - START_TIME); + + $result['mineadmin_version'] = \Builder\Mine::getVersion(); + + $result['hyperf_version'] = HF_VERSION; + + return $result; + } + + /** + * 获取磁盘信息 + * @return array + */ + public function getDiskInfo(): array + { + $hds = explode(' ', preg_replace( + '/\s{2,}/', + ' ', + shell_exec('df -h | grep -E "^(/)"') + )); + return [ + 'total' => $hds[1], + 'usage' => $hds[2], + 'free' => $hds[3], + 'rate' => $hds[4] + ]; + } + +} diff --git a/app/System/Service/SettingConfigGroupService.php b/app/System/Service/SettingConfigGroupService.php new file mode 100644 index 0000000..7182eb4 --- /dev/null +++ b/app/System/Service/SettingConfigGroupService.php @@ -0,0 +1,37 @@ +mapper = $mapper; + } + + /** + * 删除配置组和其所属配置 + * @param int $id + * @return bool + */ + #[Transaction] + public function deleteConfigGroup(int $id): bool + { + return $this->mapper->deleteGroupAndConfig($id); + } +} \ No newline at end of file diff --git a/app/System/Service/SettingConfigService.php b/app/System/Service/SettingConfigService.php new file mode 100644 index 0000000..72392dc --- /dev/null +++ b/app/System/Service/SettingConfigService.php @@ -0,0 +1,163 @@ +mapper = $mapper; + $this->container = $container; + $this->setCacheGroupName($this->prefix . 'configGroup:'); + $this->setCacheName($this->prefix . 'config:'); + $this->redis = $this->container->get(Redis::class); + } + + /** + * 按key获取配置,并缓存 + * @param string $key + * @return array|null + * @throws \RedisException + */ + public function getConfigByKey(string $key): ?array + { + if (empty($key)) return []; + $cacheKey = $this->getCacheName() . $key; + if (($data = $this->redis->get($cacheKey))) { + return unserialize($data); + } else { + $data = $this->mapper->getConfigByKey($key); + if ($data) { + $this->redis->set($cacheKey, serialize($data)); + return $data; + } + return null; + } + } + + /** + * 清除缓存 + * @return bool + * @throws \RedisException + */ + public function clearCache(): bool + { + $groupCache = $this->redis->keys($this->getCacheGroupName().'*'); + $keyCache = $this->redis->keys($this->getCacheName().'*'); + foreach ($groupCache as $item) { + $this->redis->del($item); + } + + foreach($keyCache as $item) { + $this->redis->del($item); + } + + return true; + } + + /** + * 更新配置 + * @param string $key + * @param array $data + * @return bool + */ + public function updated(string $key, array $data): bool + { + return $this->mapper->updateConfig($key, $data); + } + + /** + * 按 keys 更新配置 + * @param array $data + * @return bool + */ + #[Transaction] + public function updatedByKeys(array $data): bool + { + foreach ($data as $name => $value) { + $this->mapper->updateByKey((string) $name, $value); + } + return true; + } + + /** + * @return string + */ + public function getCacheGroupName(): string + { + return $this->cacheGroupName; + } + + /** + * @param string $cacheGroupName + */ + public function setCacheGroupName(string $cacheGroupName): void + { + $this->cacheGroupName = $cacheGroupName; + } + + /** + * @return string + */ + public function getCacheName(): string + { + return $this->cacheName; + } + + /** + * @param string $cacheName + */ + public function setCacheName(string $cacheName): void + { + $this->cacheName = $cacheName; + } + + +} diff --git a/app/System/Service/SettingCrontabLogService.php b/app/System/Service/SettingCrontabLogService.php new file mode 100644 index 0000000..e435ee0 --- /dev/null +++ b/app/System/Service/SettingCrontabLogService.php @@ -0,0 +1,21 @@ +mapper = $mapper; + } +} \ No newline at end of file diff --git a/app/System/Service/SettingCrontabService.php b/app/System/Service/SettingCrontabService.php new file mode 100644 index 0000000..d08bf2e --- /dev/null +++ b/app/System/Service/SettingCrontabService.php @@ -0,0 +1,113 @@ +mapper = $mapper; + $this->redis = $this->container->get(Redis::class); + } + + /** + * 保存 + * @param array $data + * @return int + */ + public function save(array $data): int + { + $id = parent::save($data); + $this->redis->del($this->prefix . 'crontab'); + + return $id; + } + + /** + * 更新 + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + $res = parent::update($id, $data); + $this->redis->del($this->prefix . 'crontab'); + + return $res; + } + + /** + * 删除 + * @param array $ids + * @return bool + */ + public function delete(array $ids): bool + { + $res = parent::delete($ids); + $this->redis->del($this->prefix . 'crontab'); + + return $res; + } + + /** + * 立即执行一次定时任务 + * @param $id + * @return bool|null + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function run($id): ?bool + { + $crontab = new MineCrontab(); + $model = $this->read($id); + $crontab->setCallback($model->target); + $crontab->setType((string) $model->type); + $crontab->setEnable(true); + $crontab->setCrontabId($model->id); + $crontab->setName($model->name); + $crontab->setParameter($model->parameter ?: ''); + $crontab->setRule($model->rule); + + $executor = $this->container->get(MineExecutor::class); + + return $executor->execute($crontab, true); + } +} \ No newline at end of file diff --git a/app/System/Service/SettingGenerateColumnsService.php b/app/System/Service/SettingGenerateColumnsService.php new file mode 100644 index 0000000..7ba3744 --- /dev/null +++ b/app/System/Service/SettingGenerateColumnsService.php @@ -0,0 +1,182 @@ +mapper = $mapper; + } + + /** + * 循环插入数据 + * @param array $data + * @return int + */ + public function save(array $data): int + { + $default_column = ['created_at', 'updated_at', 'created_by', 'updated_by', 'deleted_at', 'remark']; + // 组装数据 + foreach ($data as $k => $item) { + + $column = [ + 'table_id' => $item['table_id'], + 'column_name' => $item['column_name'], + 'column_comment' => $item['column_comment'], + 'column_type' => $item['data_type'], + 'is_pk' => empty($item['column_key']) ? SettingGenerateColumns::NO : SettingGenerateColumns::YES , + 'is_required' => $item['is_nullable'] == 'NO' ? SettingGenerateColumns::YES : SettingGenerateColumns::NO, + 'query_type' => 'eq', + 'view_type' => 'text', + 'sort' => count($data) - $k, + 'allow_roles' => $item['allow_roles'] ?? null, + 'options' => $item['options'] ?? null + ]; + + // 设置默认选项 + if (!in_array($item['column_name'], $default_column) && empty($item['column_key'])) { + $column = array_merge( + $column, + [ + 'is_insert' => SettingGenerateColumns::YES, + 'is_edit' => SettingGenerateColumns::YES, + 'is_list' => SettingGenerateColumns::YES, + 'is_query' => SettingGenerateColumns::YES, + 'is_sort' => SettingGenerateColumns::NO, + ] + ); + } + + $this->mapper->save( + $this->fieldDispose($column)); + } + return 1; + } + + /** + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + $data['is_insert'] = $data['is_insert'] ? SettingGenerateColumns::YES : SettingGenerateColumns::NO; + $data['is_edit'] = $data['is_edit'] ? SettingGenerateColumns::YES : SettingGenerateColumns::NO; + $data['is_list'] = $data['is_list'] ? SettingGenerateColumns::YES : SettingGenerateColumns::NO; + $data['is_query'] = $data['is_query'] ? SettingGenerateColumns::YES : SettingGenerateColumns::NO; + $data['is_sort'] = $data['is_sort'] ? SettingGenerateColumns::YES : SettingGenerateColumns::NO; + $data['is_required'] = $data['is_required'] ? SettingGenerateColumns::YES : SettingGenerateColumns::NO; + return $this->mapper->update($id, $data); + } + + private function fieldDispose(array $column): array + { + $object = new class { + public function viewTypeDispose(&$column): void + { + switch ($column['column_type']) { + case 'varchar': + $column['query_type'] = 'like'; + $column['view_type'] = 'text'; + break; + // 富文本 + case 'text': + case 'longtext': + $column['is_list'] = SettingGenerateColumns::NO; + $column['is_query'] = SettingGenerateColumns::NO; + $column['view_type'] = 'editor'; + break; + // 日期字段 + case 'timestamp': + case 'datetime': + $column['view_type'] = 'date'; + $column['options']['mode'] = 'date'; + $column['options']['showTime'] = true; + $column['query_type'] = 'between'; + break; + case 'date': + $column['view_type'] = 'date'; + $column['options']['mode'] = 'date'; + $column['options']['showTime'] = false; + $column['query_type'] = 'between'; + break; + } + } + public function columnCommentDispose(&$column): void + { + if (preg_match('/.*:.*=.*/m', $column['column_comment'])) { + $regs = explode(':', $column['column_comment']); + $column['column_comment'] = $regs[0] ?? ''; + $column['view_type'] = 'select'; + $column['options']['collection'] = array_map(function ($item) { + $item = explode('=', $item); + return [ + 'label' => $item[1] ?? '', + 'value' => $item[0] ?? '' + ]; + }, explode(',', $regs[1] ?? '')); + } + } + public function columnName(&$column): void + { + if (stristr($column['column_name'], 'image')) { + $column['is_query'] = SettingGenerateColumns::NO; + $column['view_type'] = 'upload'; + $column['options']['type'] = 'image'; + $column['options']['multiple'] = false; + $column['query_type'] = 'eq'; + } + + if (stristr($column['column_name'], 'images')) { + $column['is_query'] = SettingGenerateColumns::NO; + $column['view_type'] = 'upload'; + $column['options']['type'] = 'image'; + $column['options']['multiple'] = true; + $column['query_type'] = 'eq'; + } + + if (stristr($column['column_name'], 'file')) { + $column['is_query'] = SettingGenerateColumns::NO; + $column['view_type'] = 'upload'; + $column['options']['type'] = 'file'; + $column['options']['multiple'] = false; + $column['query_type'] = 'eq'; + } + + if (stristr($column['column_name'], 'files')) { + $column['is_query'] = SettingGenerateColumns::NO; + $column['view_type'] = 'upload'; + $column['options']['type'] = 'file'; + $column['options']['multiple'] = true; + $column['query_type'] = 'eq'; + } + } + }; + + $object->viewTypeDispose($column); + $object->columnCommentDispose($column); + $object->columnName($column); + return $column; + } +} \ No newline at end of file diff --git a/app/System/Service/SettingGenerateTablesService.php b/app/System/Service/SettingGenerateTablesService.php new file mode 100644 index 0000000..599475e --- /dev/null +++ b/app/System/Service/SettingGenerateTablesService.php @@ -0,0 +1,377 @@ +mapper = $mapper; + $this->dataMaintainService = $dataMaintainService; + $this->settingGenerateColumnsService = $settingGenerateColumnsService; + $this->moduleService = $moduleService; + $this->container = $container; + } + + /** + * 装载数据表 + * @param array $names + * @return bool + */ + #[Transaction] + public function loadTable(array $names): bool + { + foreach ($names as $item) { + $tableInfo = [ + 'table_name' => $item['name'], + 'table_comment' => $item['comment'], + 'menu_name' => $item['comment'], + 'type' => 'single', + ]; + $id = $this->save($tableInfo); + + $columns = $this->dataMaintainService->getColumnList($item['name']); + + foreach ($columns as &$column) { + $column['table_id'] = $id; + } + $this->settingGenerateColumnsService->save($columns); + } + + return true; + } + + /** + * 同步数据表 + * @param int $id + * @return bool + */ + #[Transaction] + public function sync(int $id): bool + { + $table = $this->read($id); + $columns = $this->dataMaintainService->getColumnList( + str_replace(env('DB_PREFIX'), '', $table['table_name']) + ); + $model = $this->settingGenerateColumnsService->mapper->getModel(); + $ids = $model->newQuery()->where('table_id', $table['id'])->pluck('id'); + + $this->settingGenerateColumnsService->mapper->delete($ids->toArray()); + foreach ($columns as &$column) { + $column['table_id'] = $id; + } + $this->settingGenerateColumnsService->save($columns); + return true; + } + + /** + * 更新业务表 + * @param array $data + * @return bool + */ + #[Transaction] + public function updateTableAndColumns(array $data): bool + { + $id = $data['id']; + $columns = $data['columns']; + + unset($data['columns']); + + if (!empty($data['belong_menu_id'])) { + $data['belong_menu_id'] = is_array($data['belong_menu_id']) ? array_pop($data['belong_menu_id']) : $data['belong_menu_id']; + } else { + $data['belong_menu_id'] = 0; + } + + $data['package_name'] = empty($data['package_name']) ? null : ucfirst($data['package_name']); + $data['namespace'] = "App\\{$data['module_name']}"; + $data['generate_menus'] = implode(',', $data['generate_menus']); + + if (empty($data['options'])) { + unset($data['options']); + } + + // 更新业务表 + $this->update($id, $data); + + // 更新业务字段表 + foreach ($columns as $column) { + $this->settingGenerateColumnsService->update($column['id'], $column); + } + return true; + } + + /** + * 生成代码 + * @param array $ids + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function generate(array $ids): string + { + $this->initGenerateSetting(); + $adminId = user()->getId(); + foreach ($ids as $id) { + $this->generateCodeFile((int) $id, $adminId); + } + + return $this->packageCodeFile(); + } + + /** + * 生成步骤 + * @param int $id + * @param int $adminId + * @return SettingGenerateTables + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + protected function generateCodeFile(int $id, int $adminId): SettingGenerateTables + { + /** @var SettingGenerateTables $model */ + $model = $this->read($id); + + $classList = [ + ControllerGenerator::class, + ModelGenerator::class, + ServiceGenerator::class, + MapperGenerator::class, + RequestGenerator::class, + ApiGenerator::class, + VueIndexGenerator::class, + SqlGenerator::class, + DtoGenerator::class, + ]; + + foreach ($classList as $cls) { + $class = make($cls); + if (get_class($class) == 'Builder\Generator\SqlGenerator'){ + $class->setGenInfo($model, $adminId)->generator(); + } else { + $class->setGenInfo($model)->generator(); + } + } + + return $model; + } + + /** + * 打包代码文件 + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function packageCodeFile(): string + { + $fs = $this->container->get(Filesystem::class); + $zipFileName = BASE_PATH. '/runtime/mineadmin.zip'; + $path = BASE_PATH . '/runtime/generate'; + // 删除老的压缩包 + @unlink($zipFileName); + $archive = new \ZipArchive(); + $archive->open($zipFileName, \ZipArchive::CREATE); + $files = $fs->files($path); + foreach ($files as $file) { + $archive->addFile( + $path . '/' . $file->getFilename(), + $file->getFilename() + ); + } + $this->addZipFile($archive, $path); + $archive->close(); + return $zipFileName; + } + + protected function addZipFile(\ZipArchive $archive, string $path): void + { + $fs = $this->container->get(Filesystem::class); + foreach ($fs->directories($path) as $directory) { + if ($fs->isDirectory($directory)) { + $archive->addEmptyDir(str_replace(BASE_PATH. '/runtime/generate/', '', $directory)); + $files = $fs->files($directory); + foreach ($files as $file) { + $archive->addFile( + $directory . '/' . $file->getFilename(), + str_replace( + BASE_PATH. '/runtime/generate/', '', $directory + ) . '/' . $file->getFilename() + ); + } + $this->addZipFile($archive, $directory); + } + } + } + + /** + * 初始化生成设置 + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function initGenerateSetting(): void + { + // 设置生成目录 + $genDirectory = BASE_PATH . '/runtime/generate'; + $fs = $this->container->get(Filesystem::class); + + // 先删除再创建 + $fs->cleanDirectory($genDirectory); + $fs->deleteDirectory($genDirectory); + } + + /** + * 获取所有模型 + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getModels(): array + { + $models = []; + foreach ($this->moduleService->getModuleCache() as $item) if ($item['enabled']) { + $path = sprintf("%s/app/%s/Model/*", BASE_PATH, $item['name']); + foreach (glob($path) as $file) { + $models[] = sprintf( + '\App\%s\Model\%s', + $item['name'], + str_replace('.php', '', basename($file)) + ); + } + } + + return $models; + } + + /** + * 预览代码 + * @param int $id + * @return array + * @throws \Exception + */ + public function preview(int $id): array + { + /** @var SettingGenerateTables $model */ + $model = $this->read($id); + + return [ + [ + 'tab_name' => 'Controller.php', + 'name' => 'controller', + 'code' => make(ControllerGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'php' + ], + [ + 'tab_name' => 'Model.php', + 'name' => 'model', + 'code' => make(ModelGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'php', + ], + [ + 'tab_name' => 'Service.php', + 'name' => 'service', + 'code' => make(ServiceGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'php', + ], + [ + 'tab_name' => 'Mapper.php', + 'name' => 'mapper', + 'code' => make(MapperGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'php', + ], + [ + 'tab_name' => 'Request.php', + 'name' => 'request', + 'code' => make(RequestGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'php', + ], + [ + 'tab_name' => 'Dto.php', + 'name' => 'dto', + 'code' => make(DtoGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'php', + ], + [ + 'tab_name' => 'Api.js', + 'name' => 'api', + 'code' => make(ApiGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'javascript', + ], + [ + 'tab_name' => 'Index.vue', + 'name' => 'index', + 'code' => make(VueIndexGenerator::class)->setGenInfo($model)->preview(), + 'lang' => 'html', + ], + [ + 'tab_name' => 'Menu.sql', + 'name' => 'sql', + 'code' => make(SqlGenerator::class)->setGenInfo($model, user()->getId())->preview(), + 'lang' => 'mysql', + ], + ]; + } +} \ No newline at end of file diff --git a/app/System/Service/SystemApiColumnService.php b/app/System/Service/SystemApiColumnService.php new file mode 100644 index 0000000..13f1cd2 --- /dev/null +++ b/app/System/Service/SystemApiColumnService.php @@ -0,0 +1,25 @@ +mapper = $mapper; + } +} \ No newline at end of file diff --git a/app/System/Service/SystemApiGroupService.php b/app/System/Service/SystemApiGroupService.php new file mode 100644 index 0000000..a7cbbf2 --- /dev/null +++ b/app/System/Service/SystemApiGroupService.php @@ -0,0 +1,39 @@ +mapper = $mapper; + } + + /** + * 获取分组列表 无分页 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getList(?array $params = null, bool $isScope = true): array + { + $params['select'] = 'id, name'; + $params['status'] = SystemApiGroup::ENABLE; + return parent::getList($params, $isScope); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemApiLogService.php b/app/System/Service/SystemApiLogService.php new file mode 100644 index 0000000..d240152 --- /dev/null +++ b/app/System/Service/SystemApiLogService.php @@ -0,0 +1,25 @@ +mapper = $mapper; + } +} \ No newline at end of file diff --git a/app/System/Service/SystemApiService.php b/app/System/Service/SystemApiService.php new file mode 100644 index 0000000..52b68f1 --- /dev/null +++ b/app/System/Service/SystemApiService.php @@ -0,0 +1,33 @@ +mapper = $mapper; + } + + /** + * 获取字段列 + * @param string $id + * @return array + */ + public function getColumnListByApiId(string $id): array + { + return $this->mapper->getColumnListByApiId($id); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemAppGroupService.php b/app/System/Service/SystemAppGroupService.php new file mode 100644 index 0000000..827167e --- /dev/null +++ b/app/System/Service/SystemAppGroupService.php @@ -0,0 +1,37 @@ +mapper = $mapper; + } + + /** + * 获取分组列表 无分页 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getList(?array $params = null, bool $isScope = true): array + { + return $this->mapper->getList(['select' => ['id', 'name'], 'status' => SystemAppGroup::ENABLE], $isScope); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemAppService.php b/app/System/Service/SystemAppService.php new file mode 100644 index 0000000..be0a976 --- /dev/null +++ b/app/System/Service/SystemAppService.php @@ -0,0 +1,199 @@ +mapper = $mapper; + } + + /** + * 生成新的app id + * @return string + * @throws \Exception + */ + public function getAppId(): string + { + return bin2hex(random_bytes(5)); + } + + /** + * 生成新的app secret + * @return string + * @throws \Exception + */ + public function getAppSecret(): string + { + return base64_encode(bin2hex(random_bytes(32))); + } + + /** + * 绑定接口 + * @param int $id + * @param array $ids + * @return bool + */ + #[Transaction] + public function bind(int $id, array $ids): bool + { + return $this->mapper->bind($id, $ids); + } + + /** + * @param int|null $id + * @return array + */ + public function getApiList(?int $id): array + { + if (! $id) return []; + + return $this->mapper->getApiList($id); + } + + /** + * 获取AccessToken + * @param array $params + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function getAccessToken(array $params): array + { + if (empty($params['app_id'])) { + throw new NormalStatusException(t('mineadmin.api_auth_fail'), MineCode::API_APP_ID_MISSING); + } + + if (empty($params['signature'])) { + throw new NormalStatusException(t('mineadmin.api_auth_fail'), MineCode::API_SIGN_MISSING); + } + + $model = $this->mapper->one(function($query) use(&$params){ + $query->where('status', SystemApp::ENABLE)->where('app_id', $params['app_id']); + }); + + if (! $model) { + throw new NormalStatusException(t('mineadmin.access_denied'), MineCode::API_AUTH_EXCEPTION); + } + + if ($params['signature'] !== $this->getSignature($model['app_secret'], $params)) { + throw new NormalStatusException(t('mineadmin.api_auth_fail'), MineCode::API_IDENTITY_ERROR); + } + + $params['id'] = $model['id']; + + return ['access_token' => app_verify()->getToken($params)]; + } + + /** + * 获取签名 + * @param $appSecret + * @param $params + * @return string + */ + public function getSignature($appSecret, $params): string + { + unset($params['signature']); + + $data = [ + 'sign_ver' => ApiController::SIGN_VERSION, + 'app_secret' => $appSecret + ]; + + $data = array_merge($data, $params); + krsort($data); + + return md5(http_build_query($data)); + } + + /** + * 登录文档 + */ + public function loginDoc(string $appId, string $appSecret): int + { + $model = $this->mapper->one(function($query) use($appId, $appSecret){ + $query->where('app_id', $appId)->where('app_secret', $appSecret); + }, ['id', 'status']); + + if (! $model) { + return MineCode::API_PARAMS_ERROR; + } + + if ($model->status != SystemApp::ENABLE) { + return MineCode::APP_BAN; + } + + return MineCode::API_VERIFY_PASS; + } + + /** + * 简易验证方式 + * @param string $appId + * @param string $identity + * @return int + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function verifyEasyMode(string $appId, string $identity): int + { + $model = $this->mapper->one(function($query) use($appId){ + $query->where('app_id', $appId); + }, ['id', 'status', 'app_secret']); + + if (! $model) { + return MineCode::API_PARAMS_ERROR; + } + + if ($model->status != SystemApp::ENABLE) { + return MineCode::APP_BAN; + } + + if ($identity != md5($appId . $model->app_secret)) { + throw new NormalStatusException(t('mineadmin.api_auth_fail'), MineCode::API_SIGN_ERROR); + } + + return MineCode::API_VERIFY_PASS; + } + + /** + * 正常(复杂)验证方式 + * @param string $accessToken + * @return int + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function verifyNormalMode(string $accessToken): int + { + return app_verify()->check($accessToken) ? MineCode::API_VERIFY_PASS : MineCode::API_PARAMS_ERROR; + } + + /** + * 通过app_id获取app信息和接口数据 + * @param string $appId + * @return array + */ + public function getAppAndInterfaceList(string $appId): array + { + return $this->mapper->getAppAndInterfaceList($appId); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemDeptService.php b/app/System/Service/SystemDeptService.php new file mode 100644 index 0000000..8003dd9 --- /dev/null +++ b/app/System/Service/SystemDeptService.php @@ -0,0 +1,158 @@ +mapper = $mapper; + } + + /** + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeList(?array $params = null, bool $isScope = true): array + { + $params = array_merge(['orderBy' => 'sort', 'orderType' => 'desc'], $params); + return parent::getTreeList($params, $isScope); + } + + /** + * 获取部门领导列表 + * @param array|null $params + * @return array + */ + public function getLeaderList(?array $params = null): array + { + return $this->mapper->getLeaderList($params); + } + + /** + * 新增部门领导 + * @param array $data + * @return bool + */ + public function addLeader(array $data): bool + { + $users = []; + foreach ($data['users'] as $item) { + $users[] = array_merge(['created_at' => date('Y-m-d H:i:s')], $item); + } + return $this->mapper->addLeader((int) $data['id'], $users); + } + + /** + * 删除部门领导 + * @param array $data + * @return bool + */ + public function delLeader(array $data): bool + { + $users = []; + foreach ($data['ids'] as $id) { + $users[] = [ 'user_id' => $id ]; + } + return $this->mapper->delLeader((int) $data['id'], $users); + } + + /** + * 获取前端选择树 + * @return array + */ + public function getSelectTree(): array + { + return $this->mapper->getSelectTree(); + } + + /** + * 新增部门 + * @param array $data + * @return int + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function save(array $data): int + { + return $this->mapper->save($this->handleData($data)); + } + + /** + * 更新部门 + * @param int $id + * @param array $data + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function update(int $id, array $data): bool + { + return $this->mapper->update($id, $this->handleData($data)); + } + + /** + * 处理数据 + * @param $data + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function handleData($data): array + { + if (isset($data['id']) && $data['id'] == $data['parent_id']) { + throw new NormalStatusException(t('system.parent_dept_error'), 500); + } + + $pid = $data['parent_id'] ?? 0; + + if ($pid === 0) { + $data['level'] = $data['parent_id'] = '0'; + } else { + $data['level'] = $this->read($data['parent_id'])->level . ',' . $data['parent_id']; + } + + return $data; + } + + /** + * 真实删除部门 + * @param array $ids + * @return array|null + */ + public function realDel(array $ids): ?array + { + // 跳过的部门 + $ctuIds = []; + if (count($ids)) foreach ($ids as $id) { + if (!$this->checkChildrenExists( (int) $id)) { + $this->mapper->realDelete([$id]); + } else { + $ctuIds[] = $id; + } + } + return count($ctuIds) ? $this->mapper->getDeptName($ctuIds) : null; + } + + /** + * 检查子部门是否存在 + * @param int $id + * @return bool + */ + public function checkChildrenExists(int $id): bool + { + return $this->mapper->checkChildrenExists($id); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemDictDataService.php b/app/System/Service/SystemDictDataService.php new file mode 100644 index 0000000..97382c6 --- /dev/null +++ b/app/System/Service/SystemDictDataService.php @@ -0,0 +1,118 @@ +mapper = $mapper; + $this->container = $container; + $this->redis = $this->container->get(Redis::class); + } + + /** + * 查询多个字典 + * @param array|null $params + * @return array + * @throws \RedisException + */ + public function getLists(?array $params = null): array + { + if (! isset($params['codes'])) { + return []; + } + + $codes = explode(',', $params['codes']); + $data = []; + + foreach ($codes as $code) { + $data[$code] = $this->getList(['code' => $code]); + } + + return $data; + } + + /** + * 查询一个字典 + * @param array|null $params + * @param bool $isScope + * @return array + * @throws \RedisException + */ + public function getList(?array $params = null, bool $isScope = false): array + { + if (! isset($params['code'])) { + return []; + } + + $key = $this->prefix . 'Dict:' . $params['code']; + + if ($data = $this->redis->get($key)) { + return unserialize($data); + } + + $args = [ + 'select' => ['id', 'label as title', 'value as key'], + 'status' => \Builder\MineModel::ENABLE, + 'orderBy' => 'sort', + 'orderType' => 'desc' + ]; + $data = $this->mapper->getList(array_merge($args, $params), $isScope); + + $this->redis->set($key, serialize($data)); + + return $data; + } + + /** + * 清除缓存 + * @return bool + * @throws \RedisException + */ + public function clearCache(): bool + { + $key = $this->prefix . 'Dict:*'; + foreach ($this->redis->keys($key) as $item) { + $this->redis->del($item); + } + return true; + } +} diff --git a/app/System/Service/SystemDictTypeService.php b/app/System/Service/SystemDictTypeService.php new file mode 100644 index 0000000..8f5e1ce --- /dev/null +++ b/app/System/Service/SystemDictTypeService.php @@ -0,0 +1,26 @@ +mapper = $mapper; + } +} diff --git a/app/System/Service/SystemLoginLogService.php b/app/System/Service/SystemLoginLogService.php new file mode 100644 index 0000000..9f5f14c --- /dev/null +++ b/app/System/Service/SystemLoginLogService.php @@ -0,0 +1,25 @@ +mapper = $mapper; + } +} \ No newline at end of file diff --git a/app/System/Service/SystemMenuService.php b/app/System/Service/SystemMenuService.php new file mode 100644 index 0000000..0f282f3 --- /dev/null +++ b/app/System/Service/SystemMenuService.php @@ -0,0 +1,181 @@ +mapper = $mapper; + } + + /** + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeList(?array $params = null, bool $isScope = true): array + { + $params = array_merge(['orderBy' => 'sort', 'orderType' => 'desc'], $params); + return parent::getTreeList($params, $isScope); + } + + /** + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeListByRecycle(?array $params = null, bool $isScope = true): array + { + $params = array_merge(['orderBy' => 'sort', 'orderType' => 'desc'], $params); + return parent::getTreeListByRecycle($params, $isScope); + } + + /** + * 获取前端选择树 + * @param array $data + * @return array + */ + public function getSelectTree(array $data): array + { + return $this->mapper->getSelectTree($data); + } + + /** + * 通过code获取菜单名称 + * @param string $code + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function findNameByCode(string $code): string + { + if (strlen($code) < 1) { + return t('system.undefined_menu'); + } + $name = $this->mapper->findNameByCode($code); + return $name ?? t('system.undefined_menu'); + } + + /** + * 新增菜单 + * @param array $data + * @return int + */ + public function save(array $data): int + { + $id = $this->mapper->save($this->handleData($data)); + + // 生成RESTFUL按钮菜单 + if ($data['type'] == SystemMenu::MENUS_LIST && $data['restful'] == '1') { + $model = $this->mapper->model::find($id, ['id', 'name', 'code']); + $this->genButtonMenu($model); + } + + return $id; + } + + /** + * 生成按钮菜单 + * @param SystemMenu $model + * @return bool + */ + public function genButtonMenu(SystemMenu $model): bool + { + $buttonMenus = [ + ['name' => $model->name.'列表', 'code' => $model->code.':index'], + ['name' => $model->name.'回收站', 'code' => $model->code.':recycle'], + ['name' => $model->name.'保存', 'code' => $model->code.':save'], + ['name' => $model->name.'更新', 'code' => $model->code.':update'], + ['name' => $model->name.'删除', 'code' => $model->code.':delete'], + ['name' => $model->name.'读取', 'code' => $model->code.':read'], + ['name' => $model->name.'恢复', 'code' => $model->code.':recovery'], + ['name' => $model->name.'真实删除', 'code' => $model->code.':realDelete'], + ['name' => $model->name.'导入', 'code' => $model->code.':import'], + ['name' => $model->name.'导出', 'code' => $model->code.':export'] + ]; + + foreach ($buttonMenus as $button) { + $this->save( + array_merge( + ['parent_id' => $model->id, 'type' => SystemMenu::BUTTON], + $button + ) + ); + } + + return true; + } + + /** + * 更新菜单 + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + return $this->mapper->update($id, $this->handleData($data)); + } + + /** + * 处理数据 + * @param $data + * @return array + */ + protected function handleData($data): array + { + if (empty($data['parent_id']) || $data['parent_id'] == 0) { + $data['level'] = '0'; + $data['parent_id'] = 0; + $data['type'] = $data['type'] === SystemMenu::BUTTON ? SystemMenu::MENUS_LIST : $data['type']; + } else { + $parentMenu = $this->mapper->read((int) $data['parent_id']); + $data['level'] = $parentMenu['level'] . ',' . $parentMenu['id']; + } + return $data; + } + + /** + * 真实删除菜单 + * @return array + */ + public function realDel(array $ids): ?array + { + // 跳过的菜单 + $ctuIds = []; + if (count($ids)) foreach ($ids as $id) { + if (!$this->checkChildrenExists( (int) $id)) { + $this->mapper->realDelete([$id]); + } else { + $ctuIds[] = $id; + } + } + return count($ctuIds) ? $this->mapper->getMenuName($ctuIds) : null; + } + + /** + * 检查子菜单是否存在 + * @param int $id + * @return bool + */ + public function checkChildrenExists(int $id): bool + { + return $this->mapper->checkChildrenExists($id); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemNoticeService.php b/app/System/Service/SystemNoticeService.php new file mode 100644 index 0000000..7ed6096 --- /dev/null +++ b/app/System/Service/SystemNoticeService.php @@ -0,0 +1,64 @@ +mapper = $mapper; + } + + /** + * 保存公告 + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Throwable + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[Transaction] + public function save(array $data): int + { + $message = new QueueMessageVo(); + $message->setTitle($data['title']); + $message->setContentType( + $data['type'] === '1' + ? SystemQueueMessage::TYPE_NOTICE + : SystemQueueMessage::TYPE_ANNOUNCE + ); + $message->setContent($data['content']); + $message->setSendBy(user()->getId()); + + // 待发送用户 + $userIds = $data['users'] ?? []; + if (empty($userIds)) { + $userMapper = container()->get(SystemUserMapper::class); + $userIds = $userMapper->pluck(['status' => \Builder\MineModel::ENABLE]); + } + $data['message_id'] = push_queue_message($message, $userIds); + + if ($data['message_id'] !== -1) { + return parent::save($data); + } + + throw new NormalStatusException; + } + +} diff --git a/app/System/Service/SystemOperLogService.php b/app/System/Service/SystemOperLogService.php new file mode 100644 index 0000000..c05da0e --- /dev/null +++ b/app/System/Service/SystemOperLogService.php @@ -0,0 +1,18 @@ +mapper = $mapper; + } +} \ No newline at end of file diff --git a/app/System/Service/SystemPostService.php b/app/System/Service/SystemPostService.php new file mode 100644 index 0000000..00e1b75 --- /dev/null +++ b/app/System/Service/SystemPostService.php @@ -0,0 +1,20 @@ +mapper = $mapper; + } +} \ No newline at end of file diff --git a/app/System/Service/SystemQueueLogService.php b/app/System/Service/SystemQueueLogService.php new file mode 100644 index 0000000..66fa192 --- /dev/null +++ b/app/System/Service/SystemQueueLogService.php @@ -0,0 +1,132 @@ +mapper = $mapper; + } + + /** + * 修改队列日志的生产状态 + * @return bool + */ + public function updateProduceStatus(string $ids): bool + { + // TODO... + return true; + } + + /** + * 添加任务到队列 + * @param AmqpQueueVo $amqpQueueVo + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + public function addQueue(AmqpQueueVo $amqpQueueVo): bool + { + $producer = AnnotationCollector::get($amqpQueueVo->getProducer()); + + $class = $amqpQueueVo->getProducer(); + + if (! isset($producer['_c']['Hyperf\Amqp\Annotation\Producer'])) { + throw new NormalStatusException(t('system.queue_annotation_not_open'), 500); + } + + return $this->producer->produce(new $class($amqpQueueVo->getData()), + $amqpQueueVo->getIsConfirm(), + $amqpQueueVo->getTimeout(), + $amqpQueueVo->getDelayTime() + ); + } + + /** + * 推送消息到队列 + * @param QueueMessageVo $message + * @param array $receiveUsers + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + public function pushMessage(QueueMessageVo $message, array $receiveUsers = []): bool + { + $producer = AnnotationCollector::get(\App\System\Queue\Producer\MessageProducer::class); + $consumer = AnnotationCollector::get(\App\System\Queue\Consumer\MessageConsumer::class); + + if (! isset($producer['_c']['Hyperf\Amqp\Annotation\Producer']) || ! isset($consumer['_c']['Hyperf\Amqp\Annotation\Consumer'])) { + throw new NormalStatusException(t('system.queue_annotation_not_open'), 500); + } + + if (empty ($message->getTitle())) { + throw new NormalStatusException(t('system.queue_missing_message_title'), 500); + } + + if (empty ($message->getContent())) { + throw new NormalStatusException(t('system.queue_missing_message_content_type'), 500); + } + + if (empty ($message->getContentType())) { + throw new NormalStatusException(t('system.queue_missing_content'), 500); + } + + if (empty($receiveUsers)) { + $receiveUsers = $this->userService->pluck(['status' => SystemUser::USER_NORMAL],'id'); + } + + $data = [ + 'title' => $message->getTitle(), + 'content' => $message->getContent(), + 'content_type' => $message->getContentType(), + 'send_by' => $message->getSendBy() ?: user()->getId(), + 'receive_users' => $receiveUsers, + ]; + + return $this->producer->produce( + new MessageProducer($data), + $message->getIsConfirm(), + $message->getTimeout(), + $message->getDelayTime() + ); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemQueueMessageService.php b/app/System/Service/SystemQueueMessageService.php new file mode 100644 index 0000000..2812779 --- /dev/null +++ b/app/System/Service/SystemQueueMessageService.php @@ -0,0 +1,108 @@ +mapper = $mapper; + } + + /** + * 获取用户未读消息 + * @param int $id + * @return mixed + */ + public function getUnreadMessage(int $id) + { + $params = [ + 'user_id' => $id, + 'orderBy' => 'created_at', + 'orderType' => 'desc', + 'getReceive' => true, + 'read_status' => 1, + ]; + return $this->mapper->getPageList($params, false); + } + + /** + * 获取收信箱列表数据 + * @param array $params + * @return array + */ + public function getReceiveMessage(array $params = []): array + { + $params['getReceive'] = true; + unset($params['getSend']); + return $this->mapper->getPageList($params, false); + } + + /** + * 获取已发送列表数据 + * @param array $params + * @return array + */ + public function getSendMessage(array $params = []): array + { + $params['getSend'] = true; + unset($params['getReceive']); + return $this->mapper->getPageList($params, false); + } + + /** + * 发私信 + * @param array $data + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Throwable + */ + public function sendPrivateMessage(array $data): bool + { + $queueMessage = new QueueMessageVo(); + $queueMessage->setTitle($data['title']); + $queueMessage->setContent($data['content']); + // 固定私信类型 + $queueMessage->setContentType(SystemQueueMessage::TYPE_PRIVATE_MESSAGE); + $queueMessage->setSendBy(user()->getId()); + return push_queue_message($queueMessage, $data['users']) !== -1; + } + + /** + * 获取接收人列表 + * @param int $id + * @param array $params + * @return array + */ + public function getReceiveUserList(int $id, array $params = []): array + { + return $this->mapper->getReceiveUserList($id, $params); + } + + /** + * 更新中间表数据状态 + * @param array $ids + * @param string $columnName + * @param int $value + * @return bool + */ + public function updateDataStatus(array $ids, string $columnName = 'read_status', int $value = 2): bool + { + return $this->mapper->updateDataStatus($ids, $columnName, $value); + } +} diff --git a/app/System/Service/SystemRoleService.php b/app/System/Service/SystemRoleService.php new file mode 100644 index 0000000..aae2ceb --- /dev/null +++ b/app/System/Service/SystemRoleService.php @@ -0,0 +1,86 @@ +mapper = $mapper; + } + + /** + * 获取角色列表,并过滤掉超管角色 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getList(?array $params = null, bool $isScope = true): array + { + $params['filterAdminRole'] = true; + return parent::getList($params, $isScope); + } + + public function save(array $data): int + { + if ($this->mapper->checkRoleCode($data['code'])) { + throw new NormalStatusException(t('system.rolecode_exists')); + } + return $this->mapper->save($data); + } + + /** + * 通过角色获取菜单 + * @param int $id + * @return array + */ + public function getMenuByRole(int $id): array + { + return $this->mapper->getMenuIdsByRoleIds(['ids' => $id]); + } + + /** + * 通过code获取角色名称 + * @param string $code + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function findNameByCode(string $code): string + { + if (strlen($code) < 1) { + return t('system.undefined_role'); + } + $name = $this->mapper->findNameByCode($code); + return $name ?? t('system.undefined_role'); + } + + /** + * 通过角色获取部门 + * @param int $id + * @return array + */ + public function getDeptByRole(int $id): array + { + return $this->mapper->getDeptIdsByRoleIds(['ids' => $id]); + } + + /** + * 更新角色信息 + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + return $this->mapper->update($id, $data); + } +} \ No newline at end of file diff --git a/app/System/Service/SystemUploadFileService.php b/app/System/Service/SystemUploadFileService.php new file mode 100644 index 0000000..b940752 --- /dev/null +++ b/app/System/Service/SystemUploadFileService.php @@ -0,0 +1,144 @@ +mapper = $mapper; + $this->mineUpload = $mineUpload; + } + + /** + * 上传文件 + * @param UploadedFile $uploadedFile + * @param array $config + * @return array + * @throws \League\Flysystem\FileExistsException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function upload(UploadedFile $uploadedFile, array $config = []): array + { + try { + $hash = md5_file($uploadedFile->getPath() . '/' . $uploadedFile->getFilename()); + if ($model = $this->mapper->getFileInfoByHash($hash)) { + return $model->toArray(); + } + } catch (\Exception $e) { + throw new NormalStatusException('获取文件Hash失败', 500); + } + $data = $this->mineUpload->upload($uploadedFile, $config); + if ($this->save($data)) { + return $data; + } else { + return []; + } + } + + public function chunkUpload(array $data): array + { + if ($model = $this->mapper->getFileInfoByHash($data['hash'])) { + return $model->toArray(); + } + $result = $this->mineUpload->handleChunkUpload($data); + if (isset($result['hash'])) { + $this->save($result); + } + return $result; + } + + /** + * 获取当前目录下所有文件(包含目录) + * @param array $params + * @return array + */ + public function getAllFile(array $params = []): array + { + return $this->getArrayToPageList($params); + } + + /** + * 数组数据搜索器 + * @param Collection $collect + * @param array $params + * @return Collection + */ + protected function handleArraySearch(Collection $collect, array $params): Collection + { + if ($params['name'] ?? false) { + $collect = $collect->filter(function ($row) use ($params) { + return \Builder\Helper\Str::contains($row['name'], $params['name']); + }); + } + + if ($params['label'] ?? false) { + $collect = $collect->filter(function ($row) use ($params) { + return \Builder\Helper\Str::contains($row['label'], $params['label']); + }); + } + return $collect; + } + + /** + * 保存网络图片 + * @param array $data ['url', 'path'] + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function saveNetworkImage(array $data): array + { + $data = $this->mineUpload->handleSaveNetworkImage($data); + if (! isset($data['id']) && $this->save($data)) { + return $data; + } else { + return $data; + } + } + + /** + * 通过hash获取文件信息 + * @param string $hash + * @return \Hyperf\Database\Model\Builder|\Hyperf\Database\Model\Model|object|null + */ + public function readByHash(string $hash) + { + return $this->mapper->getFileInfoByHash($hash); + } +} diff --git a/app/System/Service/SystemUserService.php b/app/System/Service/SystemUserService.php new file mode 100644 index 0000000..bcc96fb --- /dev/null +++ b/app/System/Service/SystemUserService.php @@ -0,0 +1,387 @@ +mapper = $mapper; + $this->sysMenuService = $systemMenuService; + $this->sysRoleService = $systemRoleService; + $this->container = $container; + } + + /** + * 获取验证码 + * @return string + * @throws InvalidArgumentException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getCaptcha(): string + { + $cache = container()->get(CacheInterface::class); + $captcha = new MineCaptcha(); + $info = $captcha->getCaptchaInfo(); + $key = $this->request->ip() .'-'. \Builder\Helper\Str::lower($info['code']); + $cache->set(sprintf('captcha:%s', $key), $info['code'], 60); + return $info['image']; + } + + /** + * 获取用户信息 + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getInfo(): array + { + if ( ($uid = user()->getId()) ) { + return $this->getCacheInfo((int) $uid); + } + throw new MineException(t('system.unable_get_userinfo'), 500); + } + + /** + * 获取缓存用户信息 + * @param int $id + * @return array + */ + #[Cacheable(prefix: "loginInfo", ttl: 0, value: "userId_#{id}")] + protected function getCacheInfo(int $id): array + { + $user = $this->mapper->getModel()->find($id); + $user->addHidden('deleted_at', 'password'); + $data['user'] = $user->toArray(); + if (user()->isSuperAdmin()) { + $data['roles'] = ['superAdmin']; + $data['routers'] = $this->sysMenuService->mapper->getSuperAdminRouters(); + $data['codes'] = ['*']; + } else { + $roles = $this->sysRoleService->mapper->getMenuIdsByRoleIds($user->roles()->pluck('id')->toArray()); + $ids = $this->filterMenuIds($roles); + $data['roles'] = $user->roles()->pluck('code')->toArray(); + $data['routers'] = $this->sysMenuService->mapper->getRoutersByIds($ids); + $data['codes'] = $this->sysMenuService->mapper->getMenuCode($ids); + } + + return $data; + } + + /** + * 过滤通过角色查询出来的菜单id列表,并去重 + * @param array $roleData + * @return array + */ + protected function filterMenuIds(array &$roleData): array + { + $ids = []; + foreach ($roleData as $val) { + foreach ($val['menus'] as $menu) { + $ids[] = $menu['id']; + } + } + unset($roleData); + return array_unique($ids); + } + + /** + * 新增用户 + * @param array $data + * @return int + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function save(array $data): int + { + if ($this->mapper->existsByUsername($data['username'])) { + throw new NormalStatusException(t('system.username_exists')); + } else { + $id = $this->mapper->save($this->handleData($data)); + $data['id'] = $id; + event(new UserAdd($data)); + return $id; + } + } + + /** + * 更新用户信息 + * @param int $id + * @param array $data + * @return bool + */ + #[CacheEvict(prefix: "loginInfo", value: "userId_#{id}")] + public function update(int $id, array $data): bool + { + if (isset($data['username'])) unset($data['username']); + if (isset($data['password'])) unset($data['password']); + return $this->mapper->update($id, $this->handleData($data)); + } + + /** + * 处理提交数据 + * @param $data + * @return array + */ + protected function handleData($data): array + { + if (!is_array($data['role_ids'])) { + $data['role_ids'] = explode(',', $data['role_ids']); + } + if (($key = array_search(env('ADMIN_ROLE'), $data['role_ids'])) !== false) { + unset($data['role_ids'][$key]); + } + if (!empty($data['post_ids']) && !is_array($data['post_ids'])) { + $data['post_ids'] = explode(',', $data['post_ids']); + } + if (!empty($data['dept_ids']) && !is_array($data['dept_ids'])) { + $data['dept_ids'] = explode(',', $data['dept_ids']); + } + return $data; + } + + /** + * 获取在线用户 + * @param array $params + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getOnlineUserPageList(array $params = []): array + { + $redis = redis(); + $key = sprintf('%sToken:*', config('cache.default.prefix')); + + $userIds = []; + $iterator = null; + + while (false !== ($users = $redis->scan($iterator, $key, 100))) { + foreach ($users as $user) { + if ( preg_match("/{$key}(\d+)$/", $user, $match) && isset($match[1])) { + $userIds[] = $match[1]; + } + } + unset($users); + } + + if (empty($userIds)) { + return []; + } + + return $this->getPageList(array_merge(['userIds' => $userIds ], $params)); + } + + /** + * 删除用户 + * @param array $ids + * @return bool + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function delete(array $ids): bool + { + if (!empty($ids)) { + if (($key = array_search(env('SUPER_ADMIN'), $ids)) !== false) { + unset($ids[$key]); + } + $result = $this->mapper->delete($ids); + event(new UserDelete($ids)); + return $result; + } + + return false; + } + + /** + * 真实删除用户 + * @param array $ids + * @return bool + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function realDelete(array $ids): bool + { + if (!empty($ids)) { + if (($key = array_search(env('SUPER_ADMIN'), $ids)) !== false) { + unset($ids[$key]); + } + $result = $this->mapper->realDelete($ids); + event(new UserDelete($ids)); + return $result; + } + + return false; + } + + /** + * 强制下线用户 + * @param string $id + * @return bool + * @throws InvalidArgumentException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function kickUser(string $id): bool + { + $redis = redis(); + $key = sprintf("%sToken:%s", config('cache.default.prefix'), $id); + user()->getJwt()->logout($redis->get($key), 'default'); + $redis->del($key); + return true; + } + + /** + * 初始化用户密码 + * @param int $id + * @param string $password + * @return bool + */ + public function initUserPassword(int $id, string $password = '123456'): bool + { + return $this->mapper->initUserPassword($id, $password); + } + + /** + * 清除用户缓存 + * @param string $id + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function clearCache(string $id): bool + { + $redis = redis(); + $prefix = config('cache.default.prefix'); + + $iterator = null; + while (false !== ($configKey = $redis->scan($iterator, $prefix . 'config:*', 100))) { + $redis->del($configKey); + } + while (false !== ($dictKey = $redis->scan($iterator, $prefix . 'Dict:*', 100))) { + $redis->del($dictKey); + } + $redis->del([$prefix . 'crontab', $prefix . 'modules']); + + return $redis->del("{$prefix}loginInfo:userId_{$id}") > 0; + } + + /** + * 设置用户首页 + * @param array $params + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setHomePage(array $params): bool + { + $res = ($this->mapper->getModel())::query() + ->where('id', $params['id']) + ->update(['dashboard' => $params['dashboard']]) > 0; + + $this->clearCache((string) $params['id']); + return $res; + } + + /** + * 用户更新个人资料 + * @param array $params + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function updateInfo(array $params): bool + { + if (!isset($params['id'])) { + return false; + } + + $model = $this->mapper->getModel()::find($params['id']); + unset($params['id'], $params['password']); + foreach ($params as $key => $param) { + $model[$key] = $param; + } + + $this->clearCache((string) $model['id']); + return $model->save(); + } + + /** + * 用户修改密码 + * @param array $params + * @return bool + */ + public function modifyPassword(array $params): bool + { + return $this->mapper->initUserPassword((int) user()->getId(), $params['newPassword']); + } + + /** + * 通过 id 列表获取用户基础信息 + */ + public function getUserInfoByIds(array $ids): array + { + return $this->mapper->getUserInfoByIds($ids); + } +} \ No newline at end of file diff --git a/app/System/Vo/AmqpQueueVo.php b/app/System/Vo/AmqpQueueVo.php new file mode 100644 index 0000000..cda5972 --- /dev/null +++ b/app/System/Vo/AmqpQueueVo.php @@ -0,0 +1,144 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace App\System\Vo; + +/** + * 队列内容对象 + * Class AmqpQueueVo + * @package App\System\Vo + * @author X.Mo + */ +class AmqpQueueVo +{ + /** + * 生产对象 + * @var string + */ + protected string $producer; + + /** + * 队列数据 + * @var array + */ + protected array $data; + + /** + * 是否需要确认 + * @var bool + */ + protected bool $isConfirm = false; + + /** + * 队列超时时间 + * @var integer + */ + protected int $timeout = 5; + + /** + * 队列延迟生产时间秒 + * @var integer + */ + protected int $delayTime = 0; + + /** + * @return string + */ + public function getProducer(): string + { + return $this->producer; + } + + /** + * @param string $producer + * @return AmqpQueueVo + */ + public function setProducer(string $producer): AmqpQueueVo + { + $this->producer = $producer; + return $this; + } + + /** + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * @param array $data + * @return AmqpQueueVo + */ + public function setData(array $data): AmqpQueueVo + { + $this->data = $data; + return $this; + } + + /** + * @return bool + */ + public function getIsConfirm(): bool + { + return $this->isConfirm; + } + + /** + * @param bool $isConfirm + * @return AmqpQueueVo + */ + public function setIsConfirm(bool $isConfirm): AmqpQueueVo + { + $this->isConfirm = $isConfirm; + return $this; + } + + /** + * @return int + */ + public function getTimeout(): int + { + return $this->timeout; + } + + /** + * @param int $timeout + * @return AmqpQueueVo + */ + public function setTimeout(int $timeout): AmqpQueueVo + { + $this->timeout = $timeout; + return $this; + } + + /** + * @return int + */ + public function getDelayTime(): int + { + return $this->delayTime; + } + + /** + * @param int $delayTime + * @return AmqpQueueVo + */ + public function setDelayTime(int $delayTime): AmqpQueueVo + { + $this->delayTime = $delayTime; + return $this; + } + + +} \ No newline at end of file diff --git a/app/System/Vo/QueueMessageVo.php b/app/System/Vo/QueueMessageVo.php new file mode 100644 index 0000000..f42ff90 --- /dev/null +++ b/app/System/Vo/QueueMessageVo.php @@ -0,0 +1,206 @@ +title; + } + + /** + * @param string $title + * @return $this + */ + public function setTitle(string $title): QueueMessageVo + { + $this->title = $title; + return $this; + } + + /** + * @return mixed + */ + public function getContentType(): string + { + return $this->contentType; + } + + /** + * @param string $contentType + * @return $this + */ + public function setContentType(string $contentType): QueueMessageVo + { + $this->contentType = $contentType; + return $this; + } + + /** + * @return mixed + */ + public function getContent(): string + { + return $this->content; + } + + /** + * @param string $content + * @return $this + */ + public function setContent(string $content): QueueMessageVo + { + $this->content = $content; + return $this; + } + + /** + * @return string + */ + public function getSendBy(): int + { + return $this->sendBy; + } + + /** + * @param string $sendBy + * @return QueueMessageVo + */ + public function setSendBy(int $sendBy): QueueMessageVo + { + $this->sendBy = $sendBy; + return $this; + } + + /** + * @return string + */ + public function getRemark(): string + { + return $this->remark; + } + + /** + * @param string $remark + * @return QueueMessageVo + */ + public function setRemark(string $remark): QueueMessageVo + { + $this->remark = $remark; + return $this; + } + + /** + * @return bool + */ + public function getIsConfirm(): bool + { + return $this->isConfirm; + } + + /** + * @param bool $isConfirm + * @return QueueMessageVo + */ + public function setIsConfirm(bool $isConfirm): QueueMessageVo + { + $this->isConfirm = $isConfirm; + return $this; + } + + /** + * @return int + */ + public function getTimeout(): int + { + return $this->timeout; + } + + /** + * @param int $timeout + * @return QueueMessageVo + */ + public function setTimeout(int $timeout): QueueMessageVo + { + $this->timeout = $timeout; + return $this; + } + + /** + * @return int + */ + public function getDelayTime(): int + { + return $this->delayTime; + } + + /** + * @param int $delayTime + * @return QueueMessageVo + */ + public function setDelayTime(int $delayTime): QueueMessageVo + { + $this->delayTime = $delayTime; + return $this; + } + + + +} \ No newline at end of file diff --git a/app/System/config.json b/app/System/config.json new file mode 100644 index 0000000..ae7ec57 --- /dev/null +++ b/app/System/config.json @@ -0,0 +1,9 @@ +{ + "name": "System", + "label": "系统模块", + "description": "MineAdmin核心、内置模块,提供系统管理、基础业务管理功能", + "installed": true, + "enabled": true, + "version": "1.0.0", + "order": 99 +} \ No newline at end of file diff --git a/bin/hyperf.php b/bin/hyperf.php new file mode 100644 index 0000000..a0d00b5 --- /dev/null +++ b/bin/hyperf.php @@ -0,0 +1,30 @@ +#!/usr/bin/env php +get(Hyperf\Contract\ApplicationInterface::class); + $application->run(); +})(); diff --git a/builder/Abstracts/AbstractMapper.php b/builder/Abstracts/AbstractMapper.php new file mode 100644 index 0000000..bf960c8 --- /dev/null +++ b/builder/Abstracts/AbstractMapper.php @@ -0,0 +1,64 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare (strict_types = 1); +namespace Builder\Abstracts; +use Hyperf\Context\Context; +use Builder\Traits\MapperTrait; +use Builder\MineModel; +/** + * Class AbstractMapper + * @package Builder\Abstracts + */ +abstract class AbstractMapper +{ + use MapperTrait; + + /** + * @var MineModel + */ + public $model; + + abstract public function assignModel(); + + public function __construct() + { + $this->assignModel(); + } + + /** + * 把数据设置为类属性 + * @param array $data + */ + public static function setAttributes(array $data) + { + Context::set('attributes', $data); + } + + /** + * 魔术方法,从类属性里获取数据 + * @param string $name + * @return mixed|string + */ + public function __get(string $name) + { + return $this->getAttributes()[$name] ?? ''; + } + + /** + * 获取数据 + * @return array + */ + public function getAttributes(): array + { + return Context::get('attributes', []); + } +} diff --git a/builder/Abstracts/AbstractRedis.php b/builder/Abstracts/AbstractRedis.php new file mode 100644 index 0000000..b886f72 --- /dev/null +++ b/builder/Abstracts/AbstractRedis.php @@ -0,0 +1,66 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare (strict_types = 1); +namespace Builder\Abstracts; + +use Hyperf\Config\Annotation\Value; + +/** + * Class AbstractRedis + * @package Builder\Abstracts + */ +abstract class AbstractRedis +{ + /** + * 缓存前缀 + */ + #[Value("cache.default.prefix")] + protected string $prefix; + + /** + * key 类型名 + */ + protected string $typeName; + + /** + * 获取实例 + * @return mixed + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public static function getInstance() + { + return container()->get(static::class); + } + + /** + * 获取redis实例 + * @return \Hyperf\Redis\Redis + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function redis(): \Hyperf\Redis\Redis + { + return redis(); + } + + /** + * 获取key + * @param string $key + * @return string|null + */ + public function getKey(string $key): ?string + { + return empty($key) ? null : ($this->prefix . trim($this->typeName, ':') . ':' . $key); + } + +} \ No newline at end of file diff --git a/builder/Abstracts/AbstractService.php b/builder/Abstracts/AbstractService.php new file mode 100644 index 0000000..7d03a5a --- /dev/null +++ b/builder/Abstracts/AbstractService.php @@ -0,0 +1,50 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Abstracts; +use Hyperf\Context\Context; +use Builder\Traits\ServiceTrait; + +abstract class AbstractService +{ + use ServiceTrait; + + public $mapper; + + /** + * 把数据设置为类属性 + * @param array $data + */ + public function setAttributes(array $data) + { + Context::set('attributes', $data); + } + + /** + * 魔术方法,从类属性里获取数据 + * @param string $name + * @return mixed|string + */ + public function __get(string $name) + { + return $this->getAttributes()[$name] ?? ''; + } + + /** + * 获取数据 + * @return array + */ + public function getAttributes(): array + { + return Context::get('attributes', []); + } +} diff --git a/builder/Amqp/DelayProducer.php b/builder/Amqp/DelayProducer.php new file mode 100644 index 0000000..8d92f8a --- /dev/null +++ b/builder/Amqp/DelayProducer.php @@ -0,0 +1,142 @@ +eventDispatcher = ApplicationContext::getContainer()->get(EventDispatcherInterface::class); + return retry(1, function () use ($producerMessage, $confirm, $timeout,$delayTime) { + return $this->produceMessage($producerMessage, $confirm, $timeout,$delayTime); + }); + } + + /** + * 生产消息 + * @param ProducerMessageInterface $producerMessage + * @param bool $confirm + * @param int $timeout + * @param int $delayTime + * @return bool + * @throws \Throwable + */ + private function produceMessage(ProducerMessageInterface $producerMessage, bool $confirm = false, int $timeout = 5, int $delayTime = 0): bool + { + // 连接ampq + $connection = $this->factory->getConnection($producerMessage->getPoolName()); + + $result = false; + + $this->injectMessageProperty($producerMessage); + + //触发队列发送之前事件 + $this->eventDispatcher->dispatch(new BeforeProduce($producerMessage,$delayTime)); + + //如果过期时间为0,默认过期时间1毫秒,否则为设置的过期时间 + $expiration = $delayTime == 0 ? 500 : $delayTime * 1000; + $message = new AMQPMessage($producerMessage->payload(), array_merge($producerMessage->getProperties(), [ + 'expiration' => $expiration, + ])); + + //触发队列发送之中事件 + $this->eventDispatcher->dispatch(new ProduceEvent($producerMessage)); + + try { + if ($confirm) { + $channel = $connection->getConfirmChannel(); + } else { + $channel = $connection->getChannel(); + } + + $channel->set_ack_handler(function () use (&$result) { + $result = true; + }); + //延迟配置 + $queuePrefix = strchr($producerMessage->getRoutingKey(),'.',true).'.'; + $exchangePrefix = strchr($producerMessage->getExchange(),'.',true).'.'; + $delayExchange = $exchangePrefix.'delay.exchange'; + $delayQueue = $queuePrefix.'delay.queue'; + $delayRoutingKey = $queuePrefix.'delay.routing'; + //定义延迟交换器 + $channel->exchange_declare($delayExchange, 'topic', false, true, false); + //定义延迟队列 + $channel->queue_declare($delayQueue, false, true, false, false, false, new AMQPTable(array( + "x-dead-letter-exchange" => $producerMessage->getExchange(), + "x-dead-letter-routing-key" => $producerMessage->getRoutingKey() + ))); + //绑定延迟队列到交换器上 + $channel->queue_bind($delayQueue, $delayExchange, $delayRoutingKey); + //发送消息到延迟交换器上 + $channel->basic_publish($message, $delayExchange, $delayRoutingKey); + $channel->wait_for_pending_acks_returns($timeout); + } catch (\Throwable $exception) { + //触发队列发送失败事件 + $this->eventDispatcher->dispatch(new FailToProduce($producerMessage,$exception)); + + isset($channel) && $channel->close(); + throw $exception; + } + + if ($confirm) { + $connection->releaseChannel($channel, true); + } else { + $result = true; + $connection->releaseChannel($channel); + } + //触发队列发送之后事件 + $this->eventDispatcher->dispatch(new AfterProduce($producerMessage)); + + return $result; + } + + private function injectMessageProperty(ProducerMessageInterface $producerMessage) + { + if (class_exists(AnnotationCollector::class)) { + /** @var null|\Hyperf\Amqp\Annotation\Producer $annotation */ + $annotation = AnnotationCollector::getClassAnnotation(get_class($producerMessage), \Hyperf\Amqp\Annotation\Producer::class); + if ($annotation) { + $annotation->routingKey && $producerMessage->setRoutingKey($annotation->routingKey); + $annotation->exchange && $producerMessage->setExchange($annotation->exchange); + } + } + } +} diff --git a/builder/Amqp/Event/AfterConsume.php b/builder/Amqp/Event/AfterConsume.php new file mode 100644 index 0000000..9f9e7ff --- /dev/null +++ b/builder/Amqp/Event/AfterConsume.php @@ -0,0 +1,32 @@ +message = $message; + $this->data = $data; + $this->result = $result; + } +} diff --git a/builder/Amqp/Event/AfterProduce.php b/builder/Amqp/Event/AfterProduce.php new file mode 100644 index 0000000..7a54375 --- /dev/null +++ b/builder/Amqp/Event/AfterProduce.php @@ -0,0 +1,24 @@ +producer = $producer; + } +} diff --git a/builder/Amqp/Event/BeforeConsume.php b/builder/Amqp/Event/BeforeConsume.php new file mode 100644 index 0000000..c5db9d5 --- /dev/null +++ b/builder/Amqp/Event/BeforeConsume.php @@ -0,0 +1,30 @@ +message = $message; + $this->data = $data; + } +} diff --git a/builder/Amqp/Event/BeforeProduce.php b/builder/Amqp/Event/BeforeProduce.php new file mode 100644 index 0000000..ed7032b --- /dev/null +++ b/builder/Amqp/Event/BeforeProduce.php @@ -0,0 +1,26 @@ +producer = $producer; + $this->delayTime = $delayTime; + } +} diff --git a/builder/Amqp/Event/ConsumeEvent.php b/builder/Amqp/Event/ConsumeEvent.php new file mode 100644 index 0000000..9293bca --- /dev/null +++ b/builder/Amqp/Event/ConsumeEvent.php @@ -0,0 +1,29 @@ +message = $message; + $this->data = $data; + } +} diff --git a/builder/Amqp/Event/FailToConsume.php b/builder/Amqp/Event/FailToConsume.php new file mode 100644 index 0000000..566a8ca --- /dev/null +++ b/builder/Amqp/Event/FailToConsume.php @@ -0,0 +1,41 @@ +throwable = $throwable; + $this->message = $message; + $this->data = $data; + + } + + public function getThrowable(): Throwable + { + return $this->throwable; + } +} diff --git a/builder/Amqp/Event/FailToProduce.php b/builder/Amqp/Event/FailToProduce.php new file mode 100644 index 0000000..9bb7a68 --- /dev/null +++ b/builder/Amqp/Event/FailToProduce.php @@ -0,0 +1,34 @@ +throwable = $throwable; + } + + public function getThrowable(): Throwable + { + return $this->throwable; + } +} diff --git a/builder/Amqp/Event/ProduceEvent.php b/builder/Amqp/Event/ProduceEvent.php new file mode 100644 index 0000000..53917cd --- /dev/null +++ b/builder/Amqp/Event/ProduceEvent.php @@ -0,0 +1,32 @@ +producer = $producer; + } + + public function getProducer(): ProducerMessageInterface + { + return $this->producer; + } +} diff --git a/builder/Amqp/Event/WaitTimeout.php b/builder/Amqp/Event/WaitTimeout.php new file mode 100644 index 0000000..e4eca27 --- /dev/null +++ b/builder/Amqp/Event/WaitTimeout.php @@ -0,0 +1,16 @@ +service = container()->get(SystemQueueLogService::class); + if ($event->message) { + $class = get_class($event); + $func = lcfirst(trim(strrchr($class, '\\'),'\\')); + $this->$func($event); + } + } + + /** + * Description:消费前 + * User:mike + * @param object $event + */ + public function beforeConsume(object $event) + { + $this->service->update( + (int)$event->data['queue_id'], + ['consume_status' => SystemQueueLog::CONSUME_STATUS_DOING] + ); + } + + /** + * Description:消费中 + * User:mike + * @param object $event + */ + public function consumeEvent(object $event) + { + // TODO... + } + + /** + * Description:消费后 + * User:mike + * @param object $event + */ + public function afterConsume(object $event) + { + $this->service->update( + (int)$event->data['queue_id'], + ['consume_status' => SystemQueueLog::CONSUME_STATUS_SUCCESS] + ); + } + + /** + * Description:消费失败 + * User:mike + * @param object $event + */ + public function failToConsume(object $event) + { + $this->service->update( + (int)$event->data['queue_id'], [ + 'consume_status' => SystemQueueLog::CONSUME_STATUS_REPEAT, + 'log_content' => $event->throwable ?: $event->throwable->getMessage() + ]); + } +} diff --git a/builder/Amqp/Listener/QueueProduceListener.php b/builder/Amqp/Listener/QueueProduceListener.php new file mode 100644 index 0000000..ed67ab7 --- /dev/null +++ b/builder/Amqp/Listener/QueueProduceListener.php @@ -0,0 +1,137 @@ +service = container()->get(SystemQueueLogService::class); + $class = get_class($event); + $func = lcfirst(trim(strrchr($class, '\\'),'\\')); + $this->$func($event); + } + + /** + * Description:生产前 + * User:mike, x.mo + * @param object $event + */ + public function beforeProduce(object $event) + { + $queueName = strchr($event->producer->getRoutingKey(), '.', true) . '.queue'; + + $id = $this->service->save([ + 'exchange_name' => $event->producer->getExchange(), + 'routing_key_name' => $event->producer->getRoutingKey(), + 'queue_name' => $queueName, + 'queue_content' => $event->producer->payload(), + 'delay_time' => $event->delayTime ?? 0, + 'produce_status' => SystemQueueLog::PRODUCE_STATUS_SUCCESS + ]); + + $this->setId($id); + + $payload = json_decode($event->producer->payload(), true); + + if (!isset($payload['queue_id'])) { + $event->producer->setPayload([ + 'queue_id' => $id, 'data' => $payload + ]); + } + + $this->service->update($id, [ 'queue_content' => $event->producer->payload() ]); + } + + /** + * Description:生产中 + * User:mike, x.mo + * @param object $event + */ + public function produceEvent(object $event): void + { + // TODO... + } + + /** + * Description:生产后 + * User:mike, x.mo + * @param object $event + */ + public function afterProduce(object $event): void + { + if (isset($event->producer) && $event->producer instanceof MessageProducer) { + (new SystemQueueMessageMapper)->save( + json_decode($event->producer->payload(), true)['data'] + ); + } + } + + /** + * Description:生产失败 + * User:mike, x.mo + */ + public function failToProduce(object $event): void + { + $this->service->update((int) $this->getId(), [ + 'produce_status' => SystemQueueLog::PRODUCE_STATUS_FAIL, + 'log_content' => $event->throwable ?: $event->throwable->getMessage() + ]); + } + + public function setId(int $id): void + { + Context::set('id', $id); + } + + public function getId(): int + { + return Context::get('id', 0); + } +} diff --git a/builder/Amqp/README.md b/builder/Amqp/README.md new file mode 100644 index 0000000..530c0cd --- /dev/null +++ b/builder/Amqp/README.md @@ -0,0 +1,32 @@ +

1.创建生产者:

+php bin/hyperf.php gen:amqp-producer DemoProducer +

2.创建消费者:

+php bin/hyperf.php gen:amqp-consumer DemoConsumer +

3.修改生产者的注解,例如:

+

两个注解最好都要修改

+Producer(exchange: 'hyperf.exchange', routingKey: 'role.routing') +

4.修改消费者的注解,例如:

+

两个注解最好都要修改

+Consumer(exchange: 'hyperf.exchange', routingKey: 'role.routing', queue: 'role.queue', name: "role.queue", nums: 1)] +

5.依赖注入延迟队列类

+use Hyperf\Di\Annotation\Inject; +
+use Builder\Amqp\DelayProducer; +
+/** +
+ * @Inject +
+ * @var DelayProducer +
+ */ +
+protected $producer; +

6.进行调用

+$message = new RoleProducer('message'); +
+参数1:生产者对象 2:队列的是否确认机制 3:超时时间重试 (秒) 4:延迟时间 (秒) 如果为0或者不传,默认立即发送 +
+$this->producer->produce($message,false,5,5); +
+其他的问题请见hyperf官方的AMQP模块文档 diff --git a/builder/Annotation/Auth.php b/builder/Annotation/Auth.php new file mode 100644 index 0000000..03744cf --- /dev/null +++ b/builder/Annotation/Auth.php @@ -0,0 +1,37 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 用户登录验证。 + * @Annotation + * @Target({"CLASS","METHOD"}) + */ +#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] +class Auth extends AbstractAnnotation +{ + /** + * scene + * @var string + */ + public string $scene; + + public function __construct($value = 'default') + { + parent::__construct($value); + $this->bindMainProperty('scene', [ $value ]); + } +} \ No newline at end of file diff --git a/builder/Annotation/DeleteCache.php b/builder/Annotation/DeleteCache.php new file mode 100644 index 0000000..bd010a0 --- /dev/null +++ b/builder/Annotation/DeleteCache.php @@ -0,0 +1,39 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 删除缓存。 + * @Annotation + * @Target({"METHOD"}) + */ +#[Attribute(Attribute::TARGET_METHOD)] +class DeleteCache extends AbstractAnnotation { + + /** + * 缓存key,多个以逗号分开 + * @var string + */ + public string $keys; + + public function __construct($value = null) + { + parent::__construct($value); + $this->bindMainProperty('keys', [ $value ]); + } + + +} \ No newline at end of file diff --git a/builder/Annotation/ExcelData.php b/builder/Annotation/ExcelData.php new file mode 100644 index 0000000..ab2f09a --- /dev/null +++ b/builder/Annotation/ExcelData.php @@ -0,0 +1,24 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * excel导入导出元数据。 + * @Annotation + * @Target("CLASS") + */ +#[Attribute(Attribute::TARGET_CLASS)] +class ExcelData extends AbstractAnnotation {} \ No newline at end of file diff --git a/builder/Annotation/ExcelProperty.php b/builder/Annotation/ExcelProperty.php new file mode 100644 index 0000000..f16659d --- /dev/null +++ b/builder/Annotation/ExcelProperty.php @@ -0,0 +1,84 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * excel导入导出元数据。 + * @Annotation + * @Target("PROPERTY") + */ +#[Attribute(Attribute::TARGET_PROPERTY)] +class ExcelProperty extends AbstractAnnotation +{ + /** + * 列表头名称 + * @var string + */ + public string $value; + + /** + * 列顺序 + * @var int + */ + public int $index; + + /** + * 宽度 + * @var int + */ + public int $width; + + /** + * 对齐方式,默认居左 + * @var string + */ + public string $align; + + /** + * 列表头字体颜色 + * @var string + */ + public string $headColor; + + /** + * 列表头背景颜色 + * @var string + */ + public string $headBgColor; + + /** + * 列表体字体颜色 + * @var string + */ + public string $color; + + /** + * 列表体背景颜色 + * @var string + */ + public string $bgColor; + + /** + * 字典数据列表 + */ + public ?array $dictData = null; + + /** + * 字典名称 + * @var string + */ + public string $dictName; +} \ No newline at end of file diff --git a/builder/Annotation/OperationLog.php b/builder/Annotation/OperationLog.php new file mode 100644 index 0000000..fd08096 --- /dev/null +++ b/builder/Annotation/OperationLog.php @@ -0,0 +1,37 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 记录操作日志注解。 + * @Annotation + * @Target({"METHOD"}) + */ +#[Attribute(Attribute::TARGET_METHOD)] +class OperationLog extends AbstractAnnotation +{ + /** + * 菜单名称 + * @var string + */ + public string $menuName; + + public function __construct($value = '') + { + parent::__construct($value); + $this->bindMainProperty('menuName', [ $value ]); + } +} \ No newline at end of file diff --git a/builder/Annotation/Permission.php b/builder/Annotation/Permission.php new file mode 100644 index 0000000..306e736 --- /dev/null +++ b/builder/Annotation/Permission.php @@ -0,0 +1,48 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 用户权限验证。 + * @Annotation + * @Target({"METHOD"}) + */ +#[Attribute(Attribute::TARGET_METHOD)] +class Permission extends AbstractAnnotation { + + /** + * 菜单代码 + * @var string + */ + public string $code; + + /** + * 多个菜单代码,过滤条件 + * 为 OR 时,检查有一个通过则全部通过 + * 为 AND 时,检查有一个不通过则全不通过 + * @var string + */ + public string $where; + + public function __construct($value = null, $where = 'OR') + { + parent::__construct($value); + $this->bindMainProperty('code', [ $value ]); + $this->bindMainProperty('where', [ $where ]); + } + + +} \ No newline at end of file diff --git a/builder/Annotation/Resubmit.php b/builder/Annotation/Resubmit.php new file mode 100644 index 0000000..7df5b88 --- /dev/null +++ b/builder/Annotation/Resubmit.php @@ -0,0 +1,44 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 禁止重复提交 + * @Annotation + * @Target({"METHOD"}) + */ +#[Attribute(Attribute::TARGET_METHOD)] +class Resubmit extends AbstractAnnotation +{ + /** + * second + * @var int + */ + public int $second = 3; + + /** + * 提示信息 + * @var string + */ + public string $message; + + public function __construct($value, $message = null) + { + parent::__construct($value); + $this->bindMainProperty('second', [ $value ]); + $this->bindMainProperty('message', [ $message ]); + } +} \ No newline at end of file diff --git a/builder/Annotation/Role.php b/builder/Annotation/Role.php new file mode 100644 index 0000000..ba3bd84 --- /dev/null +++ b/builder/Annotation/Role.php @@ -0,0 +1,48 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 用户角色验证。 + * @Annotation + * @Target({"METHOD"}) + */ +#[Attribute(Attribute::TARGET_METHOD)] +class Role extends AbstractAnnotation { + + /** + * 角色代码标识 + * @var string + */ + public string $code; + + /** + * 多个角色代码,过滤条件 + * 为 OR 时,检查有一个通过则全部通过 + * 为 AND 时,检查有一个不通过则全不通过 + * @var string + */ + public string $where; + + public function __construct($value = null, $where = 'OR') + { + parent::__construct($value); + $this->bindMainProperty('code', [ $value ]); + $this->bindMainProperty('where', [ $where ]); + } + + +} \ No newline at end of file diff --git a/builder/Annotation/Transaction.php b/builder/Annotation/Transaction.php new file mode 100644 index 0000000..81d2276 --- /dev/null +++ b/builder/Annotation/Transaction.php @@ -0,0 +1,37 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Annotation; + +use Attribute; +use Hyperf\Di\Annotation\AbstractAnnotation; + +/** + * 数据库事务注解。 + * @Annotation + * @Target({"METHOD"}) + */ +#[Attribute(Attribute::TARGET_METHOD)] +class Transaction extends AbstractAnnotation +{ + /** + * retry 重试次数 + * @var int + */ + public int $retry = 1; + + public function __construct($value = 1) + { + parent::__construct($value); + $this->bindMainProperty('retry', [ $value ]); + } +} \ No newline at end of file diff --git a/builder/Aspect/AuthAspect.php b/builder/Aspect/AuthAspect.php new file mode 100644 index 0000000..b684179 --- /dev/null +++ b/builder/Aspect/AuthAspect.php @@ -0,0 +1,58 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; +use Builder\Annotation\Auth; +use Builder\Exception\TokenException; + +/** + * Class AuthAspect + * @package Builder\Aspect + */ +#[Aspect] +class AuthAspect extends AbstractAspect +{ + + public array $annotations = [ + Auth::class + ]; + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + /** @var Auth $auth */ + if (isset($proceedingJoinPoint->getAnnotationMetadata()->method[Auth::class])) { + $auth = $proceedingJoinPoint->getAnnotationMetadata()->method[Auth::class]; + } + + $scene = $auth->scene ?? 'default'; + + $loginUser = user($scene); + + if (! $loginUser->check(null, $scene)) { + throw new TokenException(t('jwt.validate_fail')); + } + + return $proceedingJoinPoint->process(); + } +} \ No newline at end of file diff --git a/builder/Aspect/ConsumeAspect.php b/builder/Aspect/ConsumeAspect.php new file mode 100644 index 0000000..96f6773 --- /dev/null +++ b/builder/Aspect/ConsumeAspect.php @@ -0,0 +1,50 @@ +getArguments()[0]; + $message = $proceedingJoinPoint->getArguments()[1]; + try{ + event(new BeforeConsume($message, $data)); + $result = $proceedingJoinPoint->process(); + event(new AfterConsume($message, $data, $result)); + return $result; + } catch (\Throwable $e) { + event(new FailToConsume($message, $data, $e)); + return null; + } + } +} diff --git a/builder/Aspect/DeleteCacheAspect.php b/builder/Aspect/DeleteCacheAspect.php new file mode 100644 index 0000000..aad9dfc --- /dev/null +++ b/builder/Aspect/DeleteCacheAspect.php @@ -0,0 +1,74 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use Hyperf\Config\Annotation\Value; +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; +use Builder\Annotation\DeleteCache; +use Builder\Helper\Str; + +/** + * Class DeleteCacheAspect + * @package Builder\Aspect + */ +#[Aspect] +class DeleteCacheAspect extends AbstractAspect +{ + public array $annotations = [ + DeleteCache::class + ]; + + /** + * 缓存前缀 + */ + #[Value("cache.default.prefix")] + protected string $prefix; + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + $redis = redis(); + + /** @var DeleteCache $deleteCache */ + $deleteCache = $proceedingJoinPoint->getAnnotationMetadata()->method[DeleteCache::class]; + + $result = $proceedingJoinPoint->process(); + + if ( !empty($deleteCache->keys)) { + $keys = explode(',', $deleteCache->keys); + $iterator = null; + $n = []; + foreach ($keys as $key) { + if (! Str::contains($key, '*')) { + $n[] = $this->prefix . $key; + } else { + while (false !== ($k = $redis->scan($iterator, $this->prefix . $key, 100))) { + $redis->del($k); + } + } + } + $redis->del($n); + } + + return $result; + } +} \ No newline at end of file diff --git a/builder/Aspect/OperationLogAspect.php b/builder/Aspect/OperationLogAspect.php new file mode 100644 index 0000000..78abdba --- /dev/null +++ b/builder/Aspect/OperationLogAspect.php @@ -0,0 +1,123 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use App\System\Service\SystemMenuService; +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Psr\Container\ContainerInterface; +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Http\Message\ResponseInterface; + +use Builder\Annotation\OperationLog; +use Builder\Annotation\Permission; +use Builder\Event\Operation; +use Builder\Helper\LoginUser; +use Builder\Helper\Str; +use Builder\MineRequest; + + +/** + * Class OperationLogAspect + * @package Builder\Aspect + */ +#[Aspect] +class OperationLogAspect extends AbstractAspect +{ + public array $annotations = [ + OperationLog::class + ]; + + /** + * 容器 + */ + protected ContainerInterface $container; + + public function __construct() + { + $this->container = container(); + } + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed|void + * @throws \Hyperf\Di\Exception\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + $annotation = $proceedingJoinPoint->getAnnotationMetadata()->method[OperationLog::class]; + /* @var $result ResponseInterface */ + $result = $proceedingJoinPoint->process(); + $isDownload = false; + if (! empty($annotation->menuName) || ($annotation = $proceedingJoinPoint->getAnnotationMetadata()->method[Permission::class])) { + if (!empty($result->getHeader('content-description')) && !empty($result->getHeader('content-transfer-encoding'))) { + $isDownload = true; + } + $evDispatcher = $this->container->get(EventDispatcherInterface::class); + $evDispatcher->dispatch(new Operation($this->getRequestInfo([ + 'code' => !empty($annotation->code) ? explode(',', $annotation->code)[0] : '', + 'name' => $annotation->menuName ?? '', + 'response_code' => $result->getStatusCode(), + 'response_data' => $isDownload ? '文件下载' : $result->getBody()->getContents() + ]))); + } + return $result; + } + + /** + * @param array $data + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function getRequestInfo(array $data): array + { + $request = $this->container->get(MineRequest::class); + $loginUser = $this->container->get(LoginUser::class); + + $operationLog = [ + 'time' => date('Y-m-d H:i:s', $request->getServerParams()['request_time']), + 'method' => $request->getServerParams()['request_method'], + 'router' => $request->getServerParams()['path_info'], + 'protocol' => $request->getServerParams()['server_protocol'], + 'ip' => $request->ip(), + 'ip_location' => Str::ipToRegion($request->ip()), + 'service_name' => $data['name'] ?: $this->getOperationMenuName($data['code']), + 'request_data' => $request->all(), + 'response_code' => $data['response_code'], + 'response_data' => $data['response_data'], + ]; + try { + $operationLog['username'] = $loginUser->getUsername(); + } catch (\Exception $e) { + $operationLog['username'] = t('system.no_login_user'); + } + + return $operationLog; + } + + /** + * 获取菜单名称 + * @param string $code + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function getOperationMenuName(string $code): string + { + return $this->container->get(SystemMenuService::class)->findNameByCode($code); + } +} \ No newline at end of file diff --git a/builder/Aspect/PermissionAspect.php b/builder/Aspect/PermissionAspect.php new file mode 100644 index 0000000..5ba7dbf --- /dev/null +++ b/builder/Aspect/PermissionAspect.php @@ -0,0 +1,134 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use App\System\Service\SystemUserService; +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; + +use Builder\Annotation\Permission; +use Builder\Exception\NoPermissionException; +use Builder\Helper\LoginUser; +use Builder\MineRequest; + +/** + * Class PermissionAspect + * @package Builder\Aspect + */ +#[Aspect] +class PermissionAspect extends AbstractAspect +{ + public array $annotations = [ + Permission::class + ]; + + /** + * SystemUserService + */ + protected SystemUserService $service; + + /** + * MineRequest + */ + protected MineRequest $request; + + /** + * LoginUser + */ + protected LoginUser $loginUser; + + /** + * PermissionAspect constructor. + * @param SystemUserService $service + * @param MineRequest $request + * @param LoginUser $loginUser + */ + public function __construct( + SystemUserService $service, + MineRequest $request, + LoginUser $loginUser + ) + { + $this->service = $service; + $this->request = $request; + $this->loginUser = $loginUser; + } + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + if ($this->loginUser->isSuperAdmin()) { + return $proceedingJoinPoint->process(); + } + + /** @var Permission $permission */ + if (isset($proceedingJoinPoint->getAnnotationMetadata()->method[Permission::class])) { + $permission = $proceedingJoinPoint->getAnnotationMetadata()->method[Permission::class]; + } + + // 注解权限为空,则放行 + if (empty($permission->code)) { + return $proceedingJoinPoint->process(); + } + + $this->checkPermission($permission->code, $permission->where); + + return $proceedingJoinPoint->process(); + } + + /** + * 检查权限 + * @param string $codeString + * @param string $where + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function checkPermission(string $codeString, string $where): bool + { + $codes = $this->service->getInfo()['codes']; + + if ($where === 'OR') { + foreach (explode(',', $codeString) as $code) { + if (in_array(trim($code), $codes)) { + return true; + } + } + throw new NoPermissionException( + t('system.no_permission') . ' -> [ ' . $this->request->getPathInfo() . ' ]' + ); + } + + if ($where === 'AND') { + foreach (explode(',', $codeString) as $code) { + $code = trim($code); + if (! in_array($code, $codes)) { + $service = container()->get(\App\System\Service\SystemMenuService::class); + throw new NoPermissionException( + t('system.no_permission') . ' -> [ ' . $service->findNameByCode($code) . ' ]' + ); + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/builder/Aspect/ResubmitAspect.php b/builder/Aspect/ResubmitAspect.php new file mode 100644 index 0000000..9c047fa --- /dev/null +++ b/builder/Aspect/ResubmitAspect.php @@ -0,0 +1,67 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; + +use Builder\Annotation\Resubmit; +use Builder\Exception\NormalStatusException; +use Builder\MineRequest; +use Builder\Redis\MineLockRedis; + +/** + * Class ResubmitAspect + * @package Builder\Aspect + */ +#[Aspect] +class ResubmitAspect extends AbstractAspect +{ + + public array $annotations = [ + Resubmit::class + ]; + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws \Throwable + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + /** @var $resubmit Resubmit*/ + if (isset($proceedingJoinPoint->getAnnotationMetadata()->method[Resubmit::class])) { + $resubmit = $proceedingJoinPoint->getAnnotationMetadata()->method[Resubmit::class]; + } + + $request = container()->get(MineRequest::class); + + $key = md5(sprintf('%s-%s-%s', $request->ip(), $request->getPathInfo(), $request->getMethod())); + + $lockRedis = new MineLockRedis(); + $lockRedis->setTypeName('resubmit'); + + if ($lockRedis->check($key)) { + $lockRedis = null; + throw new NormalStatusException($resubmit->message ?: t('mineadmin.resubmit'), 500); + } + + $lockRedis->lock($key, $resubmit->second); + $lockRedis = null; + + return $proceedingJoinPoint->process(); + } +} \ No newline at end of file diff --git a/builder/Aspect/RoleAspect.php b/builder/Aspect/RoleAspect.php new file mode 100644 index 0000000..aa54862 --- /dev/null +++ b/builder/Aspect/RoleAspect.php @@ -0,0 +1,135 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use App\System\Service\SystemUserService; +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; + +use Builder\Annotation\Role; +use Builder\Exception\NoPermissionException; +use Builder\Helper\LoginUser; +use Builder\MineRequest; + +/** + * Class RoleAspect + * @package Builder\Aspect + */ +#[Aspect] +class RoleAspect extends AbstractAspect +{ + public array $annotations = [ + Role::class + ]; + + /** + * SystemUserService + */ + protected SystemUserService $service; + + /** + * MineRequest + */ + protected MineRequest $request; + + /** + * LoginUser + */ + protected LoginUser $loginUser; + + /** + * RoleAspect constructor. + * @param SystemUserService $service + * @param MineRequest $request + * @param LoginUser $loginUser + */ + public function __construct( + SystemUserService $service, + MineRequest $request, + LoginUser $loginUser + ) + { + $this->service = $service; + $this->request = $request; + $this->loginUser = $loginUser; + } + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + // 是超管角色放行 + if ($this->loginUser->isAdminRole()) { + return $proceedingJoinPoint->process(); + } + + /** @var Role $role */ + if (isset($proceedingJoinPoint->getAnnotationMetadata()->method[Role::class])) { + $role = $proceedingJoinPoint->getAnnotationMetadata()->method[Role::class]; + } + + // 没有使用注解,则放行 + if (empty($role->code)) { + return $proceedingJoinPoint->process(); + } + + $this->checkRole($role->code, $role->where); + + return $proceedingJoinPoint->process(); + } + + /** + * 检查角色 + * @param string $codeString + * @param string $where + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function checkRole(string $codeString, string $where): bool + { + $roles = $this->service->getInfo()['roles']; + + if ($where === 'OR') { + foreach (explode(',', $codeString) as $code) { + if (in_array(trim($code), $roles)) { + return true; + } + } + throw new NoPermissionException( + t('system.no_role') . ' -> [ ' . $codeString . ' ]' + ); + } + + if ($where === 'AND') { + foreach (explode(',', $codeString) as $code) { + $code = trim($code); + if (! in_array($code, $roles)) { + $service = container()->get(\App\System\Service\SystemRoleService::class); + throw new NoPermissionException( + t('system.no_role') . ' -> [ ' . $service->findNameByCode($code) . ' ]' + ); + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/builder/Aspect/SaveAspect.php b/builder/Aspect/SaveAspect.php new file mode 100644 index 0000000..9fd6520 --- /dev/null +++ b/builder/Aspect/SaveAspect.php @@ -0,0 +1,74 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; +use Builder\MineModel; + +/** + * Class SaveAspect + * @package Builder\Aspect + */ +#[Aspect] +class SaveAspect extends AbstractAspect +{ + public array $classes = [ + 'Builder\MineModel::save' + ]; + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + $instance = $proceedingJoinPoint->getInstance(); + + if (config('mineadmin.data_scope_enabled')) { + try { + $user = user(); + // 设置创建人 + if ($instance instanceof MineModel && + in_array('created_by', $instance->getFillable()) && + is_null($instance->created_by) + ) { + $user->check(); + $instance->created_by = $user->getId(); + } + + // 设置更新人 + if ($instance instanceof MineModel && in_array('updated_by', $instance->getFillable())) { + $user->check(); + $instance->updated_by = $user->getId(); + } + + } catch (\Throwable $e) {} + } + // 生成ID + if ($instance instanceof MineModel && + !$instance->incrementing && + $instance->getPrimaryKeyType() === 'int' && + empty($instance->{$instance->getKeyName()}) + ) { + $instance->setPrimaryKeyValue(snowflake_id()); + } + return $proceedingJoinPoint->process(); + } +} \ No newline at end of file diff --git a/builder/Aspect/TransactionAspect.php b/builder/Aspect/TransactionAspect.php new file mode 100644 index 0000000..62d2db1 --- /dev/null +++ b/builder/Aspect/TransactionAspect.php @@ -0,0 +1,61 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; + +use Hyperf\DbConnection\Db; +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Builder\Annotation\Transaction; +use Builder\Exception\MineException; + +/** + * Class TransactionAspect + * @package Builder\Aspect + */ +#[Aspect] +class TransactionAspect extends AbstractAspect +{ + public array $annotations = [ + Transaction::class + ]; + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + /** @var Transaction $transaction */ + if (isset($proceedingJoinPoint->getAnnotationMetadata()->method[Transaction::class])) { + $transaction = $proceedingJoinPoint->getAnnotationMetadata()->method[Transaction::class]; + } + try { + Db::beginTransaction(); + $number = 0; + $retry = intval($transaction->retry); + do { + $result = $proceedingJoinPoint->process(); + if (! is_null($result)) { + break; + } + ++$number; + } while ($number < $retry); + Db::commit(); + } catch (\Throwable $e) { + Db::rollBack(); + throw new MineException($e->getMessage(), 500); + } + return $result; + } +} \ No newline at end of file diff --git a/builder/Aspect/UpdateAspect.php b/builder/Aspect/UpdateAspect.php new file mode 100644 index 0000000..b7be414 --- /dev/null +++ b/builder/Aspect/UpdateAspect.php @@ -0,0 +1,57 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Aspect; +use Hyperf\Di\Annotation\Aspect; +use Hyperf\Di\Aop\AbstractAspect; +use Hyperf\Di\Aop\ProceedingJoinPoint; +use Hyperf\Di\Exception\Exception; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +use Builder\MineModel; +use Builder\MineRequest; + +/** + * Class UpdateAspect + * @package Builder\Aspect + */ +#[Aspect] +class UpdateAspect extends AbstractAspect +{ + public array $classes = [ + 'Builder\MineModel::update' + ]; + + /** + * @param ProceedingJoinPoint $proceedingJoinPoint + * @return mixed + * @throws Exception + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function process(ProceedingJoinPoint $proceedingJoinPoint) + { + $instance = $proceedingJoinPoint->getInstance(); + // 更新更改人 + if ($instance instanceof MineModel && + in_array('updated_by', $instance->getFillable()) && + config('mineadmin.data_scope_enabled') && + container()->get(MineRequest::class)->getHeaderLine('authorization') + ) { + try { + $instance->updated_by = user()->getId(); + } catch (\Throwable $e) {} + } + return $proceedingJoinPoint->process(); + } +} \ No newline at end of file diff --git a/builder/Command/Creater/CreateFormRequest.php b/builder/Command/Creater/CreateFormRequest.php new file mode 100644 index 0000000..1abcf8e --- /dev/null +++ b/builder/Command/Creater/CreateFormRequest.php @@ -0,0 +1,70 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command\Creater; + +use Hyperf\Command\Annotation\Command; +use Hyperf\Utils\Filesystem\FileNotFoundException; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\MineCommand; +use Symfony\Component\Console\Input\InputArgument; + +/** + * Class CreateFormRequest + * @package System\Command\Creater + */ +#[Command] +class CreateFormRequest extends MineCommand +{ + protected ?string $name = 'mine:request-gen'; + + protected string $module; + + public function configure() + { + parent::configure(); + $this->setHelp('run "php bin/hyperf.php mine:module "'); + $this->setDescription('Generate validate form request class file'); + $this->addArgument( + 'module_name', InputArgument::REQUIRED, + 'input module name' + ); + + $this->addArgument( + 'name', InputArgument::REQUIRED, + 'input FormRequest class file name' + ); + } + + public function handle() + { + $this->module = ucfirst(trim($this->input->getArgument('module_name'))); + $this->name = ucfirst(trim($this->input->getArgument('name'))); + + $fs = new Filesystem(); + + try { + $content = str_replace( + ['{MODULE_NAME}', '{CLASS_NAME}'], + [$this->module, $this->name], + $fs->get($this->getStub('form_request')) + ); + } catch (FileNotFoundException $e) { + $this->error($e->getMessage()); + exit; + } + + $fs->put($this->getModulePath() . $this->name . 'FormRequest.php', $content); + + $this->info("[INFO] Created request: ". $this->name . 'FormRequest.php'); + } +} diff --git a/builder/Command/Creater/CreateModel.php b/builder/Command/Creater/CreateModel.php new file mode 100644 index 0000000..32e421e --- /dev/null +++ b/builder/Command/Creater/CreateModel.php @@ -0,0 +1,100 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Command\Creater; + +use Hyperf\DbConnection\Db; +use Builder\Mine; +use Builder\MineCommand; +use Hyperf\Command\Annotation\Command; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class CreateModel + * @package System\Command\Creater + */ +#[Command] +class CreateModel extends MineCommand +{ + protected ?string $name = 'mine:model-gen'; + + public function configure() + { + parent::configure(); + $this->setHelp('run "php bin/hyperf.php mine:model-gen <--module | -M > [--table | -T [table]]"'); + $this->setDescription('Generate model to module according to data table'); + } + + public function handle() + { + $mine = make(Mine::class); + $module = $this->input->getOption('module'); + if ($module) { + $module = ucfirst(trim($this->input->getOption('module'))); + } + + $table = $this->input->getOption('table'); + if ($table) { + $table = trim($this->input->getOption('table')); + } + + if (empty($module)) { + $this->line('Missing parameter <--module < module_name>>', 'error'); + } + + $moduleInfos = $mine->getModuleInfo(); + + if (isset($moduleInfos[$module])) { + $info = $moduleInfos[$module]; + $path = "app/{$module}/Model"; + + $db = env('DB_DATABASE'); + $prefix = env('DB_PREFIX'); + + $tables = Db::select('SHOW TABLES'); + $key = "Tables_in_{$db}"; + + $tableList = []; + foreach ($tables as $k) { + $tmp = $k->$key; + if (!empty($prefix) && preg_match(sprintf("/%s_%s[_a-zA-Z0-9]+/i", $prefix, $module), $tmp)) { + $tableList[] = $tmp; + } + if (preg_match(sprintf("/%s[_a-zA-Z0-9]+/i", $module), $tmp)) { + $tableList[] = $tmp; + } + } + + if (!empty($table)) { + if (!in_array($table, $tableList)) { + $this->confirm("Table \"{$table}\" does not exist or not belong to the \"{$module}\" module. Are you sure to generate the model?", false) + && + $this->call('gen:model', ['table' => $table, '--path' => $path]); + } else { + $this->call('gen:model', ['table' => $table, '--path' => $path]); + } + } else { + foreach ($tableList as $table) { + $this->call('gen:model', ['table' => $table, '--path' => $path]); + } + } + } + } + + protected function getOptions(): array + { + return [ + ['module', '-M', InputOption::VALUE_REQUIRED, 'Please enter the module to be generated'], + ['table', '-T', InputOption::VALUE_OPTIONAL, 'Which table you want to associated with the Model.'] + ]; + } +} \ No newline at end of file diff --git a/builder/Command/Creater/Stubs/form_request.stub b/builder/Command/Creater/Stubs/form_request.stub new file mode 100644 index 0000000..69e7090 --- /dev/null +++ b/builder/Command/Creater/Stubs/form_request.stub @@ -0,0 +1,36 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace App\{MODULE_NAME}\Request; + +use Hyperf\Validation\Request\FormRequest; + +class {CLASS_NAME}FormRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + */ + public function authorize(): bool + { + return true; + } + + /** + * Get the validation rules that apply to the request. + */ + public function rules(): array + { + return [ + ]; + } +} diff --git a/builder/Command/HttpGenCommand.php b/builder/Command/HttpGenCommand.php new file mode 100644 index 0000000..72323db --- /dev/null +++ b/builder/Command/HttpGenCommand.php @@ -0,0 +1,105 @@ +exists($outputDir) && $fileSystem->deleteDirectory($outputDir); + $fileSystem->makeDirectory($outputDir); + $httpJsonFile = config('api_docs.output_dir') . "/http.json"; + if(!$fileSystem->exists($httpJsonFile)) { + throw new NormalStatusException("请先生成swagger文档"); + } + $swagger = json_decode($fileSystem->get($httpJsonFile), true); + // 生成文件 + $paths = $swagger['paths']; + foreach ($paths as $path => $methods) { + $method = array_key_first($methods); + $fileName = $methods[$method]['tags'][0] . ".http"; + $filePath = $outputDir . "/" . $fileName; + $content = + "### " . $methods[$method]['summary'] . PHP_EOL . + "// @no-log" . PHP_EOL . + Str::upper($method) . ' {{host}}' . $path . PHP_EOL . + 'Content-Type: ' . $methods[$method]['produces'][0] . PHP_EOL; + $content .= 'Authorization: Bearer {{auth_token}}' . PHP_EOL . PHP_EOL; + $params = []; + foreach ($methods[$method]['parameters'] as $param) { + $params = array_merge($params, [$param['name'] => $param['default'] ?? '']); + } + if(!empty($params)){ + $params = str_replace(["{", ",", '}'], ["{\r ", ",\r ", "\r}"], json_encode($params, JSON_UNESCAPED_UNICODE)); + $content .= $params . PHP_EOL . PHP_EOL; + } + if($path === '/system/login') { + $setToken = '> {% client.global.set("auth_token", response.body.data.token); %}' . PHP_EOL . PHP_EOL; + $content .= $setToken . PHP_EOL . PHP_EOL; + } + $this->genHttpFile($filePath, $content); + } + $this->genEnvFile(); + $this->line('http-client generate success!', 'comment'); + } + + /** + * 生成http文件 + * @param $file + * @param $content + * @return void + */ + public function genHttpFile($file, $content): void + { + $fileSystem = make(Filesystem::class); + if(!$fileSystem->exists($file)) { + $fileSystem->put($file, $content); + } else { + $fileSystem->append($file, $content); + } + } + + /** + * 生成http-client.env文件 + * @return void + */ + public function genEnvFile(): void + { + $fileSystem = make(Filesystem::class); + $host = env('HTTP_HOST', '127.0.0.1:9501'); + $content = <<put(BASE_PATH . '/runtime/http/http-client.env.json', $content); + } + +} \ No newline at end of file diff --git a/builder/Command/InstallProjectCommand.php b/builder/Command/InstallProjectCommand.php new file mode 100644 index 0000000..46936a5 --- /dev/null +++ b/builder/Command/InstallProjectCommand.php @@ -0,0 +1,369 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Command; + +use Hyperf\Command\Annotation\Command; +use Hyperf\DbConnection\Db; +use Builder\MineCommand; +use Builder\Mine; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class InstallProjectCommand + * @package System\Command + */ +#[Command] +class InstallProjectCommand extends MineCommand +{ + /** + * 安装命令 + * @var string|null + */ + protected ?string $name = 'mine:install'; + + protected const CONSOLE_GREEN_BEGIN = "\033[32;5;1m"; + protected const CONSOLE_RED_BEGIN = "\033[31;5;1m"; + protected const CONSOLE_END = "\033[0m"; + + protected array $database = []; + + protected array $redis = []; + + public function configure() + { + parent::configure(); + $this->setHelp('run "php bin/hyperf.php mine:install" install MineAdmin system'); + $this->setDescription('MineAdmin system install command'); + + $this->addOption('option', '-o', InputOption::VALUE_OPTIONAL, 'input "-o reset" is re install MineAdmin'); + } + + public function handle() + { + // 获取参数 + $option = $this->input->getOption('option'); + + // 全新安装 + if ($option === null) { + + if (!file_exists(BASE_PATH . '/.env')) { + // 欢迎 + $this->welcome(); + + // 检测环境 + $this->checkEnv(); + + // 设置数据库 + $this->setDataBaseInformationAndRedis(); + + $this->line("\n\nReset the \".env\" file. Please restart the service before running \nthe installation command to continue the installation.", "info"); + } else if (file_exists(BASE_PATH . '/.env') && $this->confirm('Do you want to continue with the installation program?', true)) { + + // 安装本地模块 + $this->installLocalModule(); + + // 其他设置 + $this->setOthers(); + + // 安装完成 + $this->finish(); + } else { + + // 欢迎 + $this->welcome(); + + // 检测环境 + $this->checkEnv(); + + // 设置数据库 + $this->setDataBaseInformationAndRedis(); + + // 安装本地模块 + $this->installLocalModule(); + + // 其他设置 + $this->setOthers(); + + // 安装完成 + $this->finish(); + } + } + + // 重新安装 + if ($option === 'reset') { + $this->line('Reinstallation is not complete...', 'error'); + } + } + + protected function welcome() + { + $this->line('-----------------------------------------------------------', 'comment'); + $this->line('Hello, welcome use MineAdmin system.', 'comment'); + $this->line('The installation is about to start, just a few steps', 'comment'); + $this->line('-----------------------------------------------------------', 'comment'); + } + + protected function checkEnv() + { + $answer = $this->confirm('Do you want to test the system environment now?', true); + + if ($answer) { + + $this->line(PHP_EOL . ' Checking environmenting...' . PHP_EOL, 'comment'); + + if (version_compare(PHP_VERSION, '8.0', '<')) { + $this->error(sprintf(' php version should >= 8.0 >>> %sNO!%s', self::CONSOLE_RED_BEGIN, self::CONSOLE_END)); + exit; + } + $this->line(sprintf(" php version %s >>> %sOK!%s", PHP_VERSION, self::CONSOLE_GREEN_BEGIN, self::CONSOLE_END)); + + $extensions = ['swoole', 'mbstring', 'json', 'openssl', 'pdo', 'xml']; + + foreach ($extensions as $ext) { + $this->checkExtension($ext); + } + } + } + + /** + * @throws \Exception + */ + protected function setDataBaseInformationAndRedis(): void + { + $dbAnswer = $this->confirm('Do you need to set Database information?', true); + // 设置数据库 + if ($dbAnswer) { + $dbchar = $this->ask('please input database charset, default:', 'utf8mb4'); + $dbname = $this->ask('please input database name, default:', 'mineadmin'); + $dbhost = $this->ask('please input database host, default:', '127.0.0.1'); + $dbport = $this->ask('please input database host port, default:', '3306'); + $prefix = $this->ask('please input table prefix, default:', 'Null'); + $dbuser = $this->ask('please input database username, default:', 'root'); + $dbpass = ''; + + $i = 3; + while ($i > 0) { + if ($i === 3) { + $dbpass = $this->ask('Please input database password. Press "enter" 3 number of times, not setting the password'); + } else { + $dbpass = $this->ask(sprintf('If you don\'t set the database password, please try again press "enter" %d number of times', $i)); + } + if (!empty($dbpass)) { + break; + } else { + $i--; + } + } + + $this->database = [ + 'charset' => $dbchar, + 'dbname' => $dbname, + 'dbhost' => $dbhost, + 'dbport' => $dbport, + 'prefix' => $prefix === 'Null' ? '' : $prefix, + 'dbuser' => $dbuser, + 'dbpass' => $dbpass ?: '', + ]; + } + + $redisAnswer = $this->confirm('Do you need to set Redis information?', true); + + // 设置Redis + if ($redisAnswer) { + $redisHost = $this->ask('please input redis host, default:', '127.0.0.1'); + $redisPort = $this->ask('please input redis host port, default:', '6379'); + $redisPass = $this->ask('please input redis password, default:', 'Null'); + $redisDb = $this->ask('please input redis db, default:', '0'); + + $this->redis = [ + 'host' => $redisHost, + 'port' => $redisPort, + 'auth' => $redisPass === 'Null' ? '(NULL)' : $redisPass, + 'db' => $redisDb, + ]; + } + + $dbAnswer && $this->generatorEnvFile(); + } + + /** + * @throws \Exception + */ + protected function generatorEnvFile() + { + try { + $env = parse_ini_file(BASE_PATH . '/.env.example', true); + $env['APP_NAME'] = 'MineAdmin'; + $env['APP_ENV'] = 'dev'; + $env['DB_DRIVER'] = 'mysql'; + $env['DB_HOST'] = $this->database['dbhost']; + $env['DB_PORT'] = $this->database['dbport']; + $env['DB_DATABASE'] = $this->database['dbname']; + $env['DB_USERNAME'] = $this->database['dbuser']; + $env['DB_PASSWORD'] = $this->database['dbpass']; + $env['DB_CHARSET'] = $this->database['charset']; + $env['DB_COLLATION'] = sprintf('%s_general_ci', $this->database['charset']); + $env['DB_PREFIX'] = $this->database['prefix']; + $env['REDIS_HOST'] = $this->redis['host']; + $env['REDIS_AUTH'] = $this->redis['auth']; + $env['REDIS_PORT'] = $this->redis['port']; + $env['REDIS_DB'] = (string) $this->redis['db']; + $env['AMQP_HOST'] = '127.0.0.7'; + $env['AMQP_PORT'] = '5672'; + $env['AMQP_USER'] = 'guest'; + $env['AMQP_PASSWORD'] = 'guest'; + $env['AMQP_VHOST'] = '/'; + $env['AMQP_ENABLE'] = 'false'; + $env['SUPER_ADMIN'] = 1; + $env['ADMIN_ROLE'] = 1; + $env['CONSOLE_SQL'] = 'true'; + $env['JWT_SECRET'] = base64_encode(random_bytes(64)); + $env['JWT_API_SECRET'] = base64_encode(random_bytes(64)); + + $id = null; + + $envContent = ''; + foreach ($env as $key => $e) { + if (!is_array($e)) { + $envContent .= sprintf('%s = %s', $key, $e === '1' ? 'true' : ($e === '' ? '' : $e)) . PHP_EOL . PHP_EOL; + } else { + $envContent .= sprintf('[%s]', $key) . PHP_EOL; + foreach ($e as $k => $v) { + $envContent .= sprintf('%s = %s', $k, $v === '1' ? 'true' : ($v === '' ? '' : $v)) . PHP_EOL; + } + $envContent .= PHP_EOL; + } + } + $dsn = sprintf("mysql:host=%s;port=%s", $this->database['dbhost'], $this->database['dbport']); + $pdo = new \PDO($dsn, $this->database['dbuser'], $this->database['dbpass']); + $isSuccess = $pdo->query( + sprintf( + 'CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARSET %s COLLATE %s_general_ci;', + $this->database['dbname'], + $this->database['charset'], + $this->database['charset'] + ) + ); + + $pdo = null; + + if ($isSuccess) { + $this->line($this->getGreenText(sprintf('"%s" database created successfully', $this->database['dbname']))); + file_put_contents(BASE_PATH . '/.env', $envContent); + } else { + $this->line($this->getRedText(sprintf('Failed to create database "%s". Please create it manually', $this->database['dbname']))); + } + } catch (\RuntimeException $e) { + $this->line($this->getRedText($e->getMessage())); + exit; + } + } + + /** + * install modules + */ + protected function installLocalModule() + { + /* @var Mine $mine */ + $this->line("Installation of local modules is about to begin...\n", 'comment'); + $mine = make(Mine::class); + $modules = $mine->getModuleInfo(); + foreach ($modules as $name => $info) { + $this->call('mine:migrate-run', ['name' => $name, '--force' => 'true']); + if ($name === 'System') { + $this->initUserData(); + } + $this->call('mine:seeder-run', ['name' => $name, '--force' => 'true']); + $this->line($this->getGreenText(sprintf('"%s" module install successfully', $name))); + } + } + + protected function setOthers() + { + $this->line(PHP_EOL . ' MineAdmin set others items...' . PHP_EOL, 'comment'); + $this->call('mine:update'); + } + + protected function initUserData() + { + // 清理数据 + Db::table('system_user')->truncate(); + Db::table('system_role')->truncate(); + Db::table('system_user_role')->truncate(); + + // 创建超级管理员 + Db::table("system_user")->insert([ + 'id' => env('SUPER_ADMIN', 1), + 'username' => 'superAdmin', + 'password' => password_hash('admin123', PASSWORD_DEFAULT), + 'user_type' => '100', + 'nickname' => '创始人', + 'email' => 'admin@adminmine.com', + 'phone' => '16858888988', + 'signed' => '广阔天地,大有所为', + 'dashboard' => 'statistics', + 'created_by' => 0, + 'updated_by' => 0, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s') + ]); + // 创建管理员角色 + Db::table('system_role')->insert([ + 'id' => env('ADMIN_ROLE', 1), + 'name' => '超级管理员(创始人)', + 'code' => 'superAdmin', + 'data_scope' => 0, + 'sort' => 0, + 'created_by' => env('SUPER_ADMIN', 0), + 'updated_by' => 0, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'remark' => '系统内置角色,不可删除' + ]); + Db::table('system_user_role')->insert([ + 'user_id' => env('SUPER_ADMIN', 1), + 'role_id' => env('ADMIN_ROLE', 1) + ]); + } + + protected function finish(): void + { + $i = 5; + $this->output->write(PHP_EOL . $this->getGreenText('The installation is almost complete'), false); + while ($i > 0) { + $this->output->write($this->getGreenText('.'), false); + $i--; + sleep(1); + } + $this->line(PHP_EOL . sprintf('%s +MineAdmin Version: %s +default username: superAdmin +default password: admin123', $this->getInfo(), Mine::getVersion()), 'comment'); + } + + /** + * @param $extension + */ + protected function checkExtension($extension): void + { + if (!extension_loaded($extension)) { + $this->line(sprintf(" %s extension not install >>> %sNO!%s", $extension, self::CONSOLE_RED_BEGIN, self::CONSOLE_END)); + exit; + } + $this->line(sprintf(' %s extension is installed >>> %sOK!%s', $extension, self::CONSOLE_GREEN_BEGIN, self::CONSOLE_END)); + } +} diff --git a/builder/Command/JwtCommand.php b/builder/Command/JwtCommand.php new file mode 100644 index 0000000..d73dbe6 --- /dev/null +++ b/builder/Command/JwtCommand.php @@ -0,0 +1,81 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command; + +use Hyperf\Command\Annotation\Command; +use Builder\Helper\Str; +use Builder\MineCommand; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class JwtCommand + * @package System\Command + */ +#[Command] +class JwtCommand extends MineCommand +{ + /** + * 生成JWT密钥命令 + * @var string|null + */ + protected ?string $name = 'mine:jwt-gen'; + + public function configure() + { + parent::configure(); + $this->setHelp('run "php bin/hyperf.php mine:gen-jwt" create the new jwt secret'); + $this->setDescription('MineAdmin system gen jwt command'); + } + + /** + * @throws \Throwable + */ + public function handle() + { + $jwtSecret = Str::upper($this->input->getOption('jwtSecret')); + + if (empty($jwtSecret)) { + $this->line('Missing parameter <--jwtSecret < jwt secret name>>', 'error'); + } + + $envPath = BASE_PATH . '/.env'; + + if (! file_exists($envPath)) { + $this->line('.env file not is exists!', 'error'); + } + + $key = base64_encode(random_bytes(64)); + + if (Str::contains(file_get_contents($envPath), $jwtSecret) === false) { + file_put_contents($envPath, "\n{$jwtSecret}={$key}\n", FILE_APPEND); + } else { + file_put_contents($envPath, preg_replace( + "~{$jwtSecret}\s*=\s*[^\n]*~", + "{$jwtSecret}=\"{$key}\"", + file_get_contents($envPath) + )); + } + + $this->info('jwt secret generator successfully:' . $key); + + } + + protected function getOptions(): array + { + return [ + ['jwtSecret', '', InputOption::VALUE_REQUIRED, 'Please enter the jwtSecret to be generated'], + ]; + } + + +} \ No newline at end of file diff --git a/builder/Command/Migrate/MineMigrate.php b/builder/Command/Migrate/MineMigrate.php new file mode 100644 index 0000000..fff6a18 --- /dev/null +++ b/builder/Command/Migrate/MineMigrate.php @@ -0,0 +1,152 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command\Migrate; + +use Hyperf\Command\Annotation\Command; +use Hyperf\Database\Commands\Migrations\TableGuesser; +use Hyperf\Database\Commands\Seeders\BaseCommand; +use Hyperf\Utils\Str; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Throwable; + +/** + * Class MineMigrate + * @package System\Command\Migrate + */ +#[Command] +class MineMigrate extends BaseCommand +{ + + protected $module; + /** + * The migration creator instance. + * + * @var MineMigrationCreator + */ + protected $creator; + + /** + * Create a new migration install command instance. + * @param MineMigrationCreator $creator + */ + public function __construct(MineMigrationCreator $creator) + { + + parent::__construct('mine:migrate-gen'); + $this->setDescription('Generate a new MineAdmin module migration file'); + $this->creator = $creator; + } + + /** + * Execute the console command. + */ + public function handle() + { + // It's possible for the developer to specify the tables to modify in this + // schema operation. The developer may also specify if this table needs + // to be freshly created so we can create the appropriate migrations. + $name = 'create_' . Str::snake(trim($this->input->getArgument('name'))).'_table'; + + $this->module = $this->input->getOption('module'); + + if (empty($this->module)) { + $this->error("<--module module_name> required"); + exit; + } + + $this->module = ucfirst($this->module); + + $table = $this->input->getOption('table'); + + $create = $this->input->getOption('create') ?: false; + + // If no table was given as an option but a create option is given then we + // will use the "create" option as the table name. This allows the devs + // to pass a table name into this option as a short-cut for creating. + if (! $table && is_string($create)) { + $table = $create; + + $create = true; + } + + // Next, we will attempt to guess the table name if this the migration has + // "create" in the name. This will allow us to provide a convenient way + // of creating migrations that create new tables for the application. + if (! $table) { + [$table, $create] = TableGuesser::guess($name); + } + + // Now we are ready to write the migration out to disk. Once we've written + // the migration out, we will dump-autoload for the entire framework to + // make sure that the migrations are registered by the class loaders. + $this->writeMigration($name, $table, $create); + } + + protected function getArguments(): array + { + return [ + ['name', InputArgument::REQUIRED, 'The name of the migration'], + ]; + } + + protected function getOptions(): array + { + return [ + ['module', '-M', InputOption::VALUE_REQUIRED, 'Please enter the module to be generated'], + ['create', null, InputOption::VALUE_OPTIONAL, 'The table to be created'], + ['table', null, InputOption::VALUE_OPTIONAL, 'The table to migrate'], + ['path', null, InputOption::VALUE_OPTIONAL, 'The location where the migration file should be created'], + ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'], + ]; + } + + /** + * Write the migration file to disk. + * @param string $name + * @param string|null $table + * @param bool $create + */ + protected function writeMigration(string $name, ?string $table, bool $create): void + { + try { + $file = pathinfo($this->creator->create( + $name, + $this->getMigrationPath(), + $table, + $create + ), PATHINFO_FILENAME); + $this->info("[INFO] Created Migration: {$file}"); + } catch (Throwable $e) { + $this->error("[ERROR] Created Migration: {$e->getMessage()}"); + } + } + + /** + * Get migration path (either specified by '--path' option or default location). + * + * @return string + */ + protected function getMigrationPath(): string + { + return BASE_PATH . '/app/' . ucfirst($this->module) . '/Database/Migrations'; + } + + /** + * Determine if the given path(s) are pre-resolved "real" paths. + */ + protected function usingRealPath(): bool + { + return $this->input->hasOption('realpath') && $this->input->getOption('realpath'); + } +} diff --git a/builder/Command/Migrate/MineMigrateRun.php b/builder/Command/Migrate/MineMigrateRun.php new file mode 100644 index 0000000..3c22e2d --- /dev/null +++ b/builder/Command/Migrate/MineMigrateRun.php @@ -0,0 +1,135 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command\Migrate; + +use Hyperf\Command\ConfirmableTrait; +use Hyperf\Database\Commands\Migrations\BaseCommand; +use Hyperf\Database\Migrations\Migrator; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Hyperf\Command\Annotation\Command; + +/** + * Class MineMigrateRun + * @package System\Command\Migrate + */ +#[Command] +class MineMigrateRun extends BaseCommand +{ + use ConfirmableTrait; + + protected ?string $name = 'mine:migrate-run'; + + /** + * The console command description. + * + * @var string + */ + protected string $description = 'Run the database migrations'; + + /** + * The migrator instance. + * + * @var Migrator + */ + protected $migrator; + + protected $module; + + /** + * Create a new migration command instance. + * @param Migrator $migrator + */ + public function __construct(Migrator $migrator) + { + parent::__construct(); + + $this->migrator = $migrator; + + $this->setDescription('The run migrate class of MineAdmin module'); + } + + /** + * Execute the console command. + */ + public function handle() + { + if (! $this->confirmToProceed()) { + return; + } + + $this->module = trim($this->input->getArgument('name')); + + $this->prepareDatabase(); + + // Next, we will check to see if a path option has been defined. If it has + // we will use the path relative to the root of this installation folder + // so that migrations may be run for any path within the applications. + $this->migrator->setOutput($this->output) + ->run($this->getMigrationPaths(), [ + 'pretend' => $this->input->getOption('pretend'), + 'step' => $this->input->getOption('step'), + ]); + + // Finally, if the "seed" option has been given, we will re-run the database + // seed task to re-populate the database, which is convenient when adding + // a migration and a seed at the same time, as it is only this command. + if ($this->input->getOption('seed') && ! $this->input->getOption('pretend')) { + $this->call('db:seed', ['--force' => true]); + } + } + + protected function getOptions(): array + { + return [ + ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'], + ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], + ['path', null, InputOption::VALUE_OPTIONAL, 'The path to the migrations files to be executed'], + ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'], + ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'], + ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run'], + ['step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually'], + ]; + } + + /** + * Prepare the migration database for running. + */ + protected function prepareDatabase() + { + $this->migrator->setConnection($this->input->getOption('database') ?? 'default'); + + if (! $this->migrator->repositoryExists()) { + $this->call('migrate:install', array_filter([ + '--database' => $this->input->getOption('database'), + ])); + } + } + + protected function getArguments(): array + { + return [ + ['name', InputArgument::REQUIRED, 'Please enter the module to be run'], + ]; + } + + /** + * Get migration path (either specified by '--path' option or default location). + * + * @return string + */ + protected function getMigrationPath(): string + { + return BASE_PATH . '/app/' . ucfirst($this->module) . '/Database/Migrations'; + } +} diff --git a/builder/Command/Migrate/MineMigrationCreator.php b/builder/Command/Migrate/MineMigrationCreator.php new file mode 100644 index 0000000..231942c --- /dev/null +++ b/builder/Command/Migrate/MineMigrationCreator.php @@ -0,0 +1,24 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Command\Migrate; + +use Hyperf\Database\Migrations\MigrationCreator; + +class MineMigrationCreator extends MigrationCreator +{ + + public function stubPath(): string + { + return BASE_PATH . '/mine/Command/Migrate/Stubs'; + } +} diff --git a/builder/Command/Migrate/Stubs/blank.stub b/builder/Command/Migrate/Stubs/blank.stub new file mode 100644 index 0000000..680ab69 --- /dev/null +++ b/builder/Command/Migrate/Stubs/blank.stub @@ -0,0 +1,34 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class DummyClass extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + // + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +} diff --git a/builder/Command/Migrate/Stubs/create.stub b/builder/Command/Migrate/Stubs/create.stub new file mode 100644 index 0000000..6baf23f --- /dev/null +++ b/builder/Command/Migrate/Stubs/create.stub @@ -0,0 +1,43 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class DummyClass extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('DummyTable', function (Blueprint $table) { + $table->engine = 'Innodb'; + $table->comment('表注释'); + $table->bigIncrements('id')->comment('主键'); + $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); + $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); + $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); + $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); + $table->addColumn('timestamp', 'deleted_at', ['precision' => 0, 'comment' => '删除时间'])->nullable(); + $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('DummyTable'); + } +} diff --git a/builder/Command/Migrate/Stubs/update.stub b/builder/Command/Migrate/Stubs/update.stub new file mode 100644 index 0000000..113c656 --- /dev/null +++ b/builder/Command/Migrate/Stubs/update.stub @@ -0,0 +1,37 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class DummyClass extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::table('DummyTable', function (Blueprint $table) { + // + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('DummyTable', function (Blueprint $table) { + // + }); + } +} diff --git a/builder/Command/MineAdmin.php b/builder/Command/MineAdmin.php new file mode 100644 index 0000000..3f974e2 --- /dev/null +++ b/builder/Command/MineAdmin.php @@ -0,0 +1,41 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command; + +use Hyperf\Command\Annotation\Command; +use Builder\MineCommand; + +/** + * Class MineAdmin + * @package System\Command + */ +#[Command] +class MineAdmin extends MineCommand +{ + protected ?string $name = 'mine'; + + public function configure() + { + parent::configure(); // TODO: Change the autogenerated stub + } + + /** + * Handle the current command. + */ + public function handle() + { + $result = shell_exec('php ' . BASE_PATH . '/bin/hyperf.php | grep mine'); + $this->line($this->getInfo(), 'comment'); + $this->line(preg_replace('/\s+Builder\s+/', '', $result), 'info'); + } +} \ No newline at end of file diff --git a/builder/Command/ModuleCommand.php b/builder/Command/ModuleCommand.php new file mode 100644 index 0000000..95ca982 --- /dev/null +++ b/builder/Command/ModuleCommand.php @@ -0,0 +1,151 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Command; + +use App\System\Service\ModuleService; +use Hyperf\Command\Annotation\Command; +use Hyperf\Command\ConfirmableTrait; +use Hyperf\Database\Migrations\Migrator; +use Builder\Helper\ConsoleTable; +use Builder\Mine; +use Builder\MineCommand; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class ModuleCommand + * @package System\Command + */ +#[Command] +class ModuleCommand extends MineCommand +{ + use ConfirmableTrait; + /** + * 安装命令 + * @var string|null + */ + protected ?string $name = 'mine:module'; + + protected Mine $mine; + + protected Migrator $migrator; + + public function __construct(Migrator $migrator) + { + parent::__construct(); + $this->migrator = $migrator; + } + + public function configure() + { + parent::configure(); + $this->mine = make(Mine::class); + $this->setHelp('run "php bin/hyperf.php mine:module --name cms --option install"'); + $this->setDescription('install command of module MineAdmin'); + $this->addOption( + 'option', null, InputOption::VALUE_OPTIONAL, + 'input "--option list" show module list, "-option install" install module or "-option uninstall" uninstall module', + 'list' + ); + $this->addOption( + 'name', null, InputOption::VALUE_OPTIONAL, + 'input module name or "list" command show module list', + ); + } + + /** + * @throws \Throwable + */ + public function handle() + { + $name = $this->input->getOption('name'); + $option = $this->input->getOption('option'); + $modules = $this->mine->getModuleInfo(); + + // 模块名不能叫list,list是展示模块列表 + if ($option === 'list') { + $table = new ConsoleTable(); + $table->setHeader(['Name', 'Description', 'Version', "Install", "Enable"]); + foreach ($modules as $mod) { + $row = [ + $mod['name'] ?? 'Null', + $mod['description'] ?? 'Null', + $mod['version'] ?? 'Null', + isset($mod['installed']) && $mod['installed'] === true ? 'yes' : 'no', + isset($mod['enabled']) && $mod['enabled'] === true ? 'yes' : 'no', + ]; + $table->addRow($row); + } + echo $table->render(); + exit; + } + + $service = make(ModuleService::class); + $name = ucfirst($name); + + // other module + if (!empty($name) && isset($modules[$name])) { + if (empty($option)) { + $this->line($this->getRedText('Please input the operation command for the module: -o install or -o uninstall')); + exit; + } + + if ($option === 'install') { + $this->call('mine:migrate-run', ['name' => $name, '--force' => 'true']); + $this->call('mine:seeder-run', ['name' => $name, '--force' => 'true']); + $this->line( + sprintf(" \"%s\" module install complete, Please run it again \"%s\" command! ", + $this->getGreenText($name), + $this->getGreenText('php bin/hyperf.php start') + ) + ); + } + + if ($option === 'uninstall') { + $input = ucfirst($name) . ' uninstall'; + $answer = $this->ask(sprintf("You are now ready to unload the module for safety. Please input: %s", $this->getRedText($input))); + if ($input !== $answer) { + $this->line('Input error'); + exit; + } + + if (! $this->confirmToProceed()) { + $this->line('A delete is already being performed'); + exit; + } + + // 是否删除数据 + if ($this->confirm("Whether to delete the data?", false)) { + $this->migrator->setOutput($this->output); + $path = $this->getUninstallPath($name); + $this->migrator->rollback([ $path ]); + is_dir($path . '/Update') && $this->migrator->rollback([ $path . '/Update']); + } + + $service->deleteModule($name); + + $this->line(sprintf("Uninstall complete, Please run it again \"%s\" command! ",$this->getGreenText('php bin/hyperf.php start'))); + } + } else { + $this->line($this->getRedText(sprintf('The "%s" module does not exist....', $name))); + } + } + + /** + * @param string $moduleName + * @return string + */ + protected function getUninstallPath(string $moduleName): string + { + return BASE_PATH . '/app/' . $moduleName . '/Database/Migrations'; + } +} diff --git a/builder/Command/Seeder/MineSeeder.php b/builder/Command/Seeder/MineSeeder.php new file mode 100644 index 0000000..26c4e52 --- /dev/null +++ b/builder/Command/Seeder/MineSeeder.php @@ -0,0 +1,112 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command\Seeder; + +use Hyperf\Command\Annotation\Command; +use Hyperf\Database\Commands\Seeders\BaseCommand; +use Hyperf\Database\Seeders\SeederCreator; +use Hyperf\Utils\Str; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class MineSeeder + * @package System\Command\Seeder + */ +#[Command] +class MineSeeder extends BaseCommand +{ + /** + * The seeder creator instance. + * + * @var SeederCreator + */ + protected SeederCreator $creator; + + protected string $module; + + /** + * Create a new seeder generator command instance. + * @param SeederCreator $creator + */ + public function __construct(SeederCreator $creator) + { + parent::__construct('mine:seeder-gen'); + $this->setDescription('Generate a new MineAdmin module seeder class'); + + $this->creator = $creator; + } + + /** + * Handle the current command. + */ + public function handle() + { + $this->module = ucfirst(trim($this->input->getOption('module'))); + $name = Str::snake(trim($this->input->getArgument('name'))); + + $this->writeMigration($name); + } + + /** + * Write the seeder file to disk. + * @param string $name + */ + protected function writeMigration(string $name) + { + $path = $this->ensureSeederDirectoryAlreadyExist( + $this->getSeederPath() + ); + + $file = pathinfo($this->creator->create($name, $path), PATHINFO_FILENAME); + + $this->info("[INFO] Created Seeder: {$file}"); + } + + protected function ensureSeederDirectoryAlreadyExist(string $path): string + { + if (! file_exists($path)) { + mkdir($path, 0755, true); + } + + return $path; + } + + protected function getArguments(): array + { + return [ + ['name', InputArgument::REQUIRED, 'The name of the seeder'], + ]; + } + + protected function getOptions(): array + { + return [ + ['module', null, InputOption::VALUE_REQUIRED, 'Please enter the module to be generated'], + ['path', null, InputOption::VALUE_OPTIONAL, 'The location where the seeder file should be created'], + ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided seeder file paths are pre-resolved absolute paths'], + ]; + } + + protected function getSeederPath(): string + { + if (! is_null($targetPath = $this->input->getOption('path'))) { + return ! $this->usingRealPath() + ? BASE_PATH . '/' . $targetPath + : $targetPath; + } + + return BASE_PATH . '/app/' . ucfirst($this->module) . '/Database/Seeders'; + } + +} diff --git a/builder/Command/Seeder/MineSeederRun.php b/builder/Command/Seeder/MineSeederRun.php new file mode 100644 index 0000000..5562864 --- /dev/null +++ b/builder/Command/Seeder/MineSeederRun.php @@ -0,0 +1,120 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Command\Seeder; + +use Hyperf\Command\Annotation\Command; +use Hyperf\Command\ConfirmableTrait; +use Hyperf\Database\Commands\Seeders\BaseCommand; +use Hyperf\Database\Seeders\Seed; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +/** + * Class MineSeederRun + * @package System\Command\Seeder + */ +#[Command] +class MineSeederRun extends BaseCommand +{ + use ConfirmableTrait; + + /** + * The console command name. + * + * @var string|null + */ + protected ?string $name = 'mine:seeder-run'; + + /** + * The console command description. + * + * @var string + */ + protected string $description = 'Seed the database with records'; + + /** + * The seed instance. + * + * @var Seed + */ + protected Seed $seed; + + protected string $module; + + /** + * Create a new seed command instance. + * @param Seed $seed + */ + public function __construct(Seed $seed) + { + parent::__construct(); + + $this->seed = $seed; + + $this->setDescription('The run seeder class of MineAdmin module'); + } + + /** + * Handle the current command. + */ + public function handle() + { + if (! $this->confirmToProceed()) { + return; + } + + $this->module = ucfirst(trim($this->input->getArgument('name'))); + + $this->seed->setOutput($this->output); + + if ($this->input->hasOption('database') && $this->input->getOption('database')) { + $this->seed->setConnection($this->input->getOption('database')); + } + + $this->seed->run([$this->getSeederPath()]); + } + + protected function getArguments(): array + { + return [ + ['name', InputArgument::REQUIRED, 'The run seeder class of the name'], + ]; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions(): array + { + return [ + ['path', null, InputOption::VALUE_OPTIONAL, 'The location where the seeders file stored'], + ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided seeder file paths are pre-resolved absolute paths'], + ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed'], + ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], + ]; + } + + + protected function getSeederPath(): string + { + if (! is_null($targetPath = $this->input->getOption('path'))) { + return ! $this->usingRealPath() + ? BASE_PATH . '/' . $targetPath + : $targetPath; + } + + return BASE_PATH . '/app/' . $this->module . '/Database/Seeders'; + } +} diff --git a/builder/Command/UpdateProjectCommand.php b/builder/Command/UpdateProjectCommand.php new file mode 100644 index 0000000..b57de0b --- /dev/null +++ b/builder/Command/UpdateProjectCommand.php @@ -0,0 +1,87 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Command; + +use Hyperf\Command\Annotation\Command; +use Hyperf\Database\Seeders\Seed; +use Hyperf\Database\Migrations\Migrator; +use Builder\MineCommand; +use Builder\Mine; + +/** + * Class UpdateProjectCommand + * @package System\Command + */ +#[Command] +class UpdateProjectCommand extends MineCommand +{ + /** + * 更新项目命令 + * @var string|null + */ + protected ?string $name = 'mine:update'; + + protected array $database = []; + + protected Seed $seed; + + protected Migrator $migrator; + + /** + * UpdateProjectCommand constructor. + * @param Migrator $migrator + * @param Seed $seed + */ + public function __construct(Migrator $migrator, Seed $seed) + { + parent::__construct(); + $this->migrator = $migrator; + $this->seed = $seed; + } + + public function configure() + { + parent::configure(); + $this->setHelp('run "php bin/hyperf.php mine:update" Update MineAdmin system'); + $this->setDescription('MineAdmin system update command'); + } + + /** + * @throws \Throwable + */ + public function handle() + { + $modules = make(Mine::class)->getModuleInfo(); + $basePath = BASE_PATH . '/app/'; + $this->migrator->setConnection('default'); + + foreach ($modules as $name => $module) { + $seedPath = $basePath . $name . '/Database/Seeders/Update'; + $migratePath = $basePath . $name . '/Database/Migrations/Update'; + + if (is_dir($migratePath)) { + $this->migrator->run([$migratePath]); + } + + if (is_dir($seedPath)) { + $this->seed->run([$seedPath]); + } + } + + redis()->flushDB(); + + $this->line($this->getGreenText('updated successfully...')); + } +} diff --git a/builder/Crontab/MineCrontab.php b/builder/Crontab/MineCrontab.php new file mode 100644 index 0000000..91b7beb --- /dev/null +++ b/builder/Crontab/MineCrontab.php @@ -0,0 +1,83 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Crontab; + +use Hyperf\Crontab\Crontab; + +class MineCrontab extends Crontab +{ + /** + * 失败策略 + * @var string + */ + protected string $failPolicy = '3'; + + /** + * 调用参数 + * @var string + */ + protected string $parameter; + + /** + * 任务ID + * @var integer + */ + protected int $crontab_id; + + /** + * @return string + */ + public function getFailPolicy(): string + { + return $this->failPolicy; + } + + /** + * @param string $failPolicy + */ + public function setFailPolicy(string $failPolicy): void + { + $this->failPolicy = $failPolicy; + } + + /** + * @return string + */ + public function getParameter(): string + { + return $this->parameter; + } + + /** + * @param string $parameter + */ + public function setParameter(string $parameter): void + { + $this->parameter = $parameter; + } + + /** + * @return int + */ + public function getCrontabId(): int + { + return $this->crontab_id; + } + + /** + * @param int $crontab_id + */ + public function setCrontabId(int $crontab_id): void + { + $this->crontab_id = $crontab_id; + } +} \ No newline at end of file diff --git a/builder/Crontab/MineCrontabManage.php b/builder/Crontab/MineCrontabManage.php new file mode 100644 index 0000000..a9a7aec --- /dev/null +++ b/builder/Crontab/MineCrontabManage.php @@ -0,0 +1,115 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab; +use Hyperf\Crontab\Parser; +use Hyperf\Di\Annotation\Inject; +use Hyperf\Guzzle\ClientFactory; +use Hyperf\Redis\Redis; +use Psr\Container\ContainerInterface; + +use App\System\Model\SettingCrontab; +use Builder\MineModel; +/** + * 定时任务管理器 + * Class MineCrontabManage + * @package Builder\Crontab + */ +class MineCrontabManage +{ + /** + * ContainerInterface + */ + #[Inject] + protected ContainerInterface $container; + + /** + * Parser + */ + #[Inject] + protected Parser $parser; + + /** + * ClientFactory + */ + #[Inject] + protected ClientFactory $clientFactory; + + /** + * Redis + */ + protected Redis $redis; + + + /** + * MineCrontabManage constructor. + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function __construct() + { + $this->redis = redis(); + } + + /** + * 获取定时任务列表 + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getCrontabList(): array + { + $prefix = config('cache.default.prefix'); + $data = $this->redis->get($prefix . 'crontab'); + + if ($data === false) { + $data = SettingCrontab::query() + ->where('status', MineModel::ENABLE) + ->get(explode(',', 'id,name,type,target,rule,parameter'))->toArray(); + $this->redis->set($prefix . 'crontab', serialize($data)); + } else { + $data = unserialize($data); + } + + if (is_null($data)) { + return []; + } + + $last = time(); + $list = []; + + foreach ($data as $item) { + + $crontab = new MineCrontab(); + $crontab->setCallback($item['target']); + $crontab->setType((string) $item['type']); + $crontab->setEnable(true); + $crontab->setCrontabId($item['id']); + $crontab->setName($item['name']); + $crontab->setParameter($item['parameter'] ?: ''); + $crontab->setRule($item['rule']); + + if (!$this->parser->isValid($crontab->getRule())) { + console()->info('Crontab task ['.$item['name'].'] rule error, skipping execution'); + continue; + } + + $time = $this->parser->parse($crontab->getRule(), $last); + if ($time) { + foreach ($time as $t) { + $list[] = clone $crontab->setExecuteTime($t); + } + } + } + return $list; + } +} \ No newline at end of file diff --git a/builder/Crontab/MineCrontabProcess.php b/builder/Crontab/MineCrontabProcess.php new file mode 100644 index 0000000..34f2d29 --- /dev/null +++ b/builder/Crontab/MineCrontabProcess.php @@ -0,0 +1,119 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Crontab; + +use Swoole\Server; +use Hyperf\Di\Annotation\Inject; +use Hyperf\Process\ProcessManager; +use Hyperf\Process\AbstractProcess; +use Psr\Container\ContainerInterface; +use Hyperf\Contract\StdoutLoggerInterface; +use Hyperf\Crontab\Strategy\StrategyInterface; +use Hyperf\Crontab\Event\CrontabDispatcherStarted; + +class MineCrontabProcess extends AbstractProcess +{ + /** + * @var string + */ + public string $name = 'CoreCrontab'; + + /** + * @var Server + */ + private $server; + + /** + * @var MineCrontabScheduler + */ + private $scheduler; + + /** + * @var StrategyInterface + */ + private $strategy; + + /** + * @var StdoutLoggerInterface + */ + private $logger; + + /** + * @var MineCrontabManage + */ + #[Inject] + protected MineCrontabManage $mineCrontabManage; + + /** + * @param ContainerInterface $container + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function __construct(ContainerInterface $container) + { + parent::__construct($container); + $this->scheduler = $container->get(MineCrontabScheduler::class); + $this->strategy = $container->get(MineCrontabStrategy::class); + $this->logger = $container->get(StdoutLoggerInterface::class); + } + + public function bind($server): void + { + $this->server = $server; + parent::bind($server); + } + + /** + * 是否自启进程 + * @param \Swoole\Coroutine\Server|\Swoole\Server $server + * @return bool + */ + public function isEnable($server): bool + { + if (!file_exists(BASE_PATH . '/.env')) { + return false; + } + return true; + } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + + */ + public function handle(): void + { + $this->event->dispatch(new CrontabDispatcherStarted()); + while (ProcessManager::isRunning()) { + $this->sleep(); + $crontabs = $this->scheduler->schedule(); + while (!$crontabs->isEmpty()) { + /** + * @var MineCrontab $crontab + */ + $crontab = $crontabs->dequeue(); + $this->strategy->dispatch($crontab); + } + } + } + + private function sleep() + { + $current = date('s', time()); + $sleep = 60 - $current; + $this->logger->debug('MineAdmin Crontab dispatcher sleep ' . $sleep . 's.'); + $sleep > 0 && \Swoole\Coroutine::sleep($sleep); + } +} diff --git a/builder/Crontab/MineCrontabScheduler.php b/builder/Crontab/MineCrontabScheduler.php new file mode 100644 index 0000000..d0d1194 --- /dev/null +++ b/builder/Crontab/MineCrontabScheduler.php @@ -0,0 +1,50 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab; + + +class MineCrontabScheduler +{ + /** + * MineCrontabManage + */ + protected MineCrontabManage $crontabManager; + + /** + * \SplQueue + */ + protected \SplQueue $schedules; + + /** + * MineCrontabScheduler constructor. + * @param MineCrontabManage $crontabManager + */ + public function __construct(MineCrontabManage $crontabManager) + { + $this->schedules = new \SplQueue(); + $this->crontabManager = $crontabManager; + } + + public function schedule(): \SplQueue + { + foreach ($this->getSchedules() ?? [] as $schedule) { + $this->schedules->enqueue($schedule); + } + return $this->schedules; + } + + protected function getSchedules(): array + { + return $this->crontabManager->getCrontabList(); + } +} diff --git a/builder/Crontab/MineCrontabStrategy.php b/builder/Crontab/MineCrontabStrategy.php new file mode 100644 index 0000000..cf3f2b0 --- /dev/null +++ b/builder/Crontab/MineCrontabStrategy.php @@ -0,0 +1,60 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab; + +use Carbon\Carbon; +use Hyperf\Di\Annotation\Inject; + +class MineCrontabStrategy +{ + /** + * MineCrontabManage + */ + #[Inject] + protected MineCrontabManage $mineCrontabManage; + + /** + * MineExecutor + */ + #[Inject] + protected MineExecutor $executor; + + /** + * @param MineCrontab $crontab + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function dispatch(MineCrontab $crontab) + { + co(function() use($crontab) { + if ($crontab->getExecuteTime() instanceof Carbon) { + $wait = $crontab->getExecuteTime()->getTimestamp() - time(); + $wait > 0 && \Swoole\Coroutine::sleep($wait); + $this->executor->execute($crontab); + } + }); + } + + /** + * 执行一次 + * @param MineCrontab $crontab + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function executeOnce(MineCrontab $crontab) + { + co(function() use($crontab) { + $this->executor->execute($crontab); + }); + } +} \ No newline at end of file diff --git a/builder/Crontab/MineExecutor.php b/builder/Crontab/MineExecutor.php new file mode 100644 index 0000000..b950d92 --- /dev/null +++ b/builder/Crontab/MineExecutor.php @@ -0,0 +1,260 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab; +use Carbon\Carbon; +use Swoole\Timer; +use Closure; +use Hyperf\Contract\ApplicationInterface; +use Hyperf\Contract\StdoutLoggerInterface; +use Hyperf\Crontab\LoggerInterface; +use Hyperf\Guzzle\ClientFactory; +use Hyperf\Utils\Coroutine; +use Psr\Container\ContainerInterface; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Builder\Crontab\Mutex\RedisServerMutex; +use Builder\Crontab\Mutex\RedisTaskMutex; +use Builder\Crontab\Mutex\ServerMutex; +use Builder\Crontab\Mutex\TaskMutex; +use Builder\MineModel; +use App\System\Model\SettingCrontab; +use App\System\Service\SettingCrontabLogService; + +class MineExecutor +{ + /** + * @var ContainerInterface + */ + protected ContainerInterface $container; + + /** + * @var Object + */ + protected Object $logger; + + /** + * @var TaskMutex + */ + protected TaskMutex $taskMutex; + + /** + * @var ServerMutex + */ + protected ServerMutex $serverMutex; + + /** + * @param ContainerInterface $container + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + if ($container->has(LoggerInterface::class)) { + $this->logger = $container->get(LoggerInterface::class); + } elseif ($container->has(StdoutLoggerInterface::class)) { + $this->logger = $container->get(StdoutLoggerInterface::class); + } + } + + /** + * 执行定时任务 + * @param MineCrontab $crontab + * @param bool $run + * @return bool|null + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function execute(MineCrontab $crontab, bool $run = false): ?bool + { + if ((! $crontab instanceof MineCrontab || ! $crontab->getExecuteTime()) && !$run) { + return null; + } + $diff = 0; + !$run && $diff = $crontab->getExecuteTime()->diffInRealSeconds(new Carbon()); + $callback = null; + switch ($crontab->getType()) { + case SettingCrontab::CLASS_CRONTAB: + $class = $crontab->getCallback(); + $method = 'execute'; + $parameters = $crontab->getParameter() ?: null; + if ($class && class_exists($class) && method_exists($class, $method)) { + $callback = function () use ($class, $method, $parameters, $crontab) { + $runnable = function () use ($class, $method, $parameters, $crontab) { + try { + $result = true; + $res = null; + $instance = make($class); + if (!empty($parameters)) { + $res = $instance->{$method}($parameters); + } else { + $res = $instance->{$method}(); + } + } catch (\Throwable $throwable) { + $result = false; + } finally { + $this->logResult($crontab, $result, isset($throwable) ? $throwable->getMessage() : $res); + } + }; + + Coroutine::create($this->decorateRunnable($crontab, $runnable)); + }; + } + break; + case SettingCrontab::COMMAND_CRONTAB: + $command = ['command' => $crontab->getCallback()]; + $parameter = $crontab->getParameter() ?: '{}'; + $input = make(ArrayInput::class, array_merge($command, json_decode($parameter, true))); + $output = make(NullOutput::class); + $application = $this->container->get(ApplicationInterface::class); + $application->setAutoExit(false); + $callback = function () use ($application, $input, $output, $crontab) { + $runnable = function () use ($application, $input, $output, $crontab) { + $result = $application->run($input, $output); + $this->logResult($crontab, $result === 0, $result); + }; + $this->decorateRunnable($crontab, $runnable)(); + }; + break; + case SettingCrontab::URL_CRONTAB: + $clientFactory = $this->container->get(ClientFactory::class); + $client = $clientFactory->create(); + $callback = function () use ($client, $crontab) { + $runnable = function () use ($client, $crontab) { + try { + $response = $client->get($crontab->getCallback()); + $result = $response->getStatusCode() === 200; + } catch (\Throwable $throwable) { + $result = false; + } + $this->logResult( + $crontab, + $result, + (!$result && isset($response)) ? $response->getBody() : '' + ); + }; + $this->decorateRunnable($crontab, $runnable)(); + }; + break; + case SettingCrontab::EVAL_CRONTAB: + $callback = function () use ($crontab) { + $runnable = function () use ($crontab) { + $result = true; + try { + eval($crontab->getCallback()); + } catch (\Throwable $throwable) { + $result = false; + } + $this->logResult($crontab, $result, isset($throwable) ? $throwable->getMessage() : ''); + }; + $this->decorateRunnable($crontab, $runnable)(); + }; + break; + } + $callback && Timer::after($diff > 0 ? $diff * 1000 : 1, $callback); + + return true; + } + + protected function runInSingleton(MineCrontab $crontab, Closure $runnable): Closure + { + return function () use ($crontab, $runnable) { + $taskMutex = $this->getTaskMutex(); + + if ($taskMutex->exists($crontab) || ! $taskMutex->create($crontab)) { + $this->logger->info(sprintf('Crontab task [%s] skipped execution at %s.', $crontab->getName(), date('Y-m-d H:i:s'))); + return; + } + + try { + $runnable(); + } finally { + $taskMutex->remove($crontab); + } + }; + } + + protected function getTaskMutex(): TaskMutex + { + if (! $this->taskMutex) { + $this->taskMutex = $this->container->has(TaskMutex::class) + ? $this->container->get(TaskMutex::class) + : $this->container->get(RedisTaskMutex::class); + } + return $this->taskMutex; + } + + protected function runOnOneServer(MineCrontab $crontab, Closure $runnable): Closure + { + return function () use ($crontab, $runnable) { + $taskMutex = $this->getServerMutex(); + + if (! $taskMutex->attempt($crontab)) { + $this->logger->info(sprintf('Crontab task [%s] skipped execution at %s.', $crontab->getName(), date('Y-m-d H:i:s'))); + return; + } + + $runnable(); + }; + } + + protected function getServerMutex(): ServerMutex + { + if (! $this->serverMutex) { + $this->serverMutex = $this->container->has(ServerMutex::class) + ? $this->container->get(ServerMutex::class) + : $this->container->get(RedisServerMutex::class); + } + return $this->serverMutex; + } + + /** + * @param MineCrontab $crontab + * @param Closure $runnable + * @return Closure + */ + protected function decorateRunnable(MineCrontab $crontab, Closure $runnable): Closure + { + if ($crontab->isSingleton()) { + $runnable = $this->runInSingleton($crontab, $runnable); + } + + if ($crontab->isOnOneServer()) { + $runnable = $this->runOnOneServer($crontab, $runnable); + } + + return $runnable; + } + + protected function logResult(MineCrontab $crontab, bool $isSuccess, $result = '') + { + if ($this->logger) { + if ($isSuccess) { + $this->logger->info(sprintf('Crontab task [%s] executed successfully at %s.', $crontab->getName(), date('Y-m-d H:i:s'))); + } else { + $this->logger->error(sprintf('Crontab task [%s] failed execution at %s.', $crontab->getName(), date('Y-m-d H:i:s'))); + } + } + $logService = $this->container->get(SettingCrontabLogService::class); + $data = [ + 'crontab_id' => $crontab->getCrontabId(), + 'name' => $crontab->getName(), + 'target' => $crontab->getCallback(), + 'parameter' => $crontab->getParameter(), + 'exception_info' => $result, + 'status' => $isSuccess ? MineModel::ENABLE : MineModel::DISABLE, + 'created_at' => date('Y-m-d H:i:s') + ]; + $logService->save($data); + } +} \ No newline at end of file diff --git a/builder/Crontab/Mutex/RedisServerMutex.php b/builder/Crontab/Mutex/RedisServerMutex.php new file mode 100644 index 0000000..289a15e --- /dev/null +++ b/builder/Crontab/Mutex/RedisServerMutex.php @@ -0,0 +1,97 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab\Mutex; + +use Hyperf\Redis\RedisFactory; +use Hyperf\Utils\Arr; +use Hyperf\Utils\Coordinator\Constants; +use Hyperf\Utils\Coordinator\CoordinatorManager; +use Hyperf\Utils\Coroutine; +use Builder\Crontab\MineCrontab; + +class RedisServerMutex implements ServerMutex +{ + /** + * @var RedisFactory + */ + private $redisFactory; + + /** + * @var null|string + */ + private $macAddress; + + public function __construct(RedisFactory $redisFactory) + { + $this->redisFactory = $redisFactory; + + $this->macAddress = $this->getMacAddress(); + } + + /** + * Attempt to obtain a server mutex for the given crontab. + * @param MineCrontab $crontab + * @return bool + */ + public function attempt(MineCrontab $crontab): bool + { + if ($this->macAddress === null) { + return false; + } + + $redis = $this->redisFactory->get($crontab->getMutexPool()); + $mutexName = $this->getMutexName($crontab); + + $result = (bool) $redis->set($mutexName, $this->macAddress, ['NX', 'EX' => $crontab->getMutexExpires()]); + + if ($result === true) { + Coroutine::create(function () use ($crontab, $redis, $mutexName) { + $exited = CoordinatorManager::until(Constants::WORKER_EXIT)->yield($crontab->getMutexExpires()); + $exited && $redis->del($mutexName); + }); + return true; + } + + return $redis->get($mutexName) === $this->macAddress; + } + + /** + * Get the server mutex for the given crontab. + * @param MineCrontab $crontab + * @return string + */ + public function get(MineCrontab $crontab): string + { + return (string) $this->redisFactory->get($crontab->getMutexPool())->get( + $this->getMutexName($crontab) + ); + } + + protected function getMutexName(MineCrontab $crontab): string + { + return 'MineAdmin' . DIRECTORY_SEPARATOR . 'crontab-' . sha1($crontab->getName() . $crontab->getRule()) . '-sv'; + } + + protected function getMacAddress(): ?string + { + $macAddresses = swoole_get_local_mac(); + + foreach (Arr::wrap($macAddresses) as $name => $address) { + if ($address && $address !== '00:00:00:00:00:00') { + return $name . ':' . str_replace(':', '', $address); + } + } + + return null; + } +} diff --git a/builder/Crontab/Mutex/RedisTaskMutex.php b/builder/Crontab/Mutex/RedisTaskMutex.php new file mode 100644 index 0000000..bc8c229 --- /dev/null +++ b/builder/Crontab/Mutex/RedisTaskMutex.php @@ -0,0 +1,71 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab\Mutex; + +use Hyperf\Redis\RedisFactory; +use Builder\Crontab\MineCrontab; + +class RedisTaskMutex implements TaskMutex +{ + /** + * @var RedisFactory + */ + private $redisFactory; + + public function __construct(RedisFactory $redisFactory) + { + $this->redisFactory = $redisFactory; + } + + /** + * Attempt to obtain a task mutex for the given crontab. + * @param MineCrontab $crontab + * @return bool + */ + public function create(MineCrontab $crontab): bool + { + return (bool) $this->redisFactory->get($crontab->getMutexPool())->set( + $this->getMutexName($crontab), + $crontab->getName(), + ['NX', 'EX' => $crontab->getMutexExpires()] + ); + } + + /** + * Determine if a task mutex exists for the given crontab. + * @param MineCrontab $crontab + * @return bool + */ + public function exists(MineCrontab $crontab): bool + { + return (bool) $this->redisFactory->get($crontab->getMutexPool())->exists( + $this->getMutexName($crontab) + ); + } + + /** + * Clear the task mutex for the given crontab. + * @param MineCrontab $crontab + */ + public function remove(MineCrontab $crontab) + { + $this->redisFactory->get($crontab->getMutexPool())->del( + $this->getMutexName($crontab) + ); + } + + protected function getMutexName(MineCrontab $crontab): string + { + return 'framework' . DIRECTORY_SEPARATOR . 'crontab-' . sha1($crontab->getName() . $crontab->getRule()); + } +} diff --git a/builder/Crontab/Mutex/ServerMutex.php b/builder/Crontab/Mutex/ServerMutex.php new file mode 100644 index 0000000..5c2a2d1 --- /dev/null +++ b/builder/Crontab/Mutex/ServerMutex.php @@ -0,0 +1,32 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab\Mutex; + +use Builder\Crontab\MineCrontab; + +interface ServerMutex +{ + /** + * Attempt to obtain a server mutex for the given crontab. + * @param MineCrontab $crontab + * @return bool + */ + public function attempt(MineCrontab $crontab): bool; + + /** + * Get the server mutex for the given crontab. + * @param MineCrontab $crontab + * @return string + */ + public function get(MineCrontab $crontab): string; +} diff --git a/builder/Crontab/Mutex/TaskMutex.php b/builder/Crontab/Mutex/TaskMutex.php new file mode 100644 index 0000000..49155e5 --- /dev/null +++ b/builder/Crontab/Mutex/TaskMutex.php @@ -0,0 +1,38 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Crontab\Mutex; + +use Builder\Crontab\MineCrontab; + +interface TaskMutex +{ + /** + * Attempt to obtain a task mutex for the given crontab. + * @param MineCrontab $crontab + * @return bool + */ + public function create(MineCrontab $crontab): bool; + + /** + * Determine if a task mutex exists for the given crontab. + * @param MineCrontab $crontab + * @return bool + */ + public function exists(MineCrontab $crontab): bool; + + /** + * Clear the task mutex for the given crontab. + * @param MineCrontab $crontab + */ + public function remove(MineCrontab $crontab); +} diff --git a/builder/Entity/EntityBean.php b/builder/Entity/EntityBean.php new file mode 100644 index 0000000..6fd19bd --- /dev/null +++ b/builder/Entity/EntityBean.php @@ -0,0 +1,34 @@ + $value) { + $method = 'set' . $this->convert_under_line($item); + if (method_exists($this, $method)) { + call_user_func([$this, $method], $value); + } + } + } + + protected function convert_under_line($str) + { + $str = preg_replace_callback('/([-_]+([a-z]{1}))/i', function ($matches) { + return strtoupper($matches[2]); + }, $str); + return $str; + } + + public function toArray() + { + return (array)$this; + } + +} \ No newline at end of file diff --git a/builder/Entity/MenuEntity.php b/builder/Entity/MenuEntity.php new file mode 100644 index 0000000..ca90f47 --- /dev/null +++ b/builder/Entity/MenuEntity.php @@ -0,0 +1,80 @@ +setMenus($menu); + } + } + + /** + * @return MenuItemEntity[] + */ + public function getMenus() + { + return $this->menus; + } + + + /** + * @return array + */ + public function toArray() + { + $menus = []; + foreach ($this->menus as $menu) { + $menus[] = $menu->toArray(); + } + return $menus; + } + + /** + * @return array + */ + public function toMenuList() + { + $menus = []; + foreach ($this->menus as $menu) { + $this->getItem($menu, $menus); + } + return $menus; + } + + private function getItem(MenuItemEntity $menu, &$menus) + { + $menus[] = [ + 'title' => $menu->getTitle(), + 'uri' => $menu->getUri(), + 'route' => $menu->getRoute() + ]; + foreach ($menu->getChildren() as $child) { + $this->getItem($child, $menus); + } + return $menus; + } + + public function addMenu(MenuItemEntity $menuItemEntity) + { + $this->menus[] = $menuItemEntity; + } + + /** + * @param mixed $menus + */ + public function setMenus($menus): void + { + $this->menus[] = new MenuItemEntity($menus); + } + +} \ No newline at end of file diff --git a/builder/Entity/MenuItemEntity.php b/builder/Entity/MenuItemEntity.php new file mode 100644 index 0000000..cc7ba60 --- /dev/null +++ b/builder/Entity/MenuItemEntity.php @@ -0,0 +1,105 @@ +id; + } + + public function getTitle(): string + { + return $this->title; + } + + public function getIcon(): ?string + { + return $this->icon; + } + + public function getUri() + { + return $this->uri; + } + + public function getRoute() + { + return $this->route; + } + + public function children(): array + { + return $this->children; + } + + /** + * @param mixed $id + */ + public function setId($id): void + { + $this->id = $id; + } + + /** + * @param mixed $title + */ + public function setTitle($title): void + { + $this->title = $title; + } + + /** + * @param mixed $icon + */ + public function setIcon($icon): void + { + $this->icon = $icon; + } + + /** + * @param mixed $uri + */ + public function setUri($uri): void + { + $this->uri = $uri; + } + + /** + * @param mixed $route + */ + public function setRoute($route): void + { + $this->route = $route; + } + + /** + * @return mixed + */ + public function getChildren() + { + return $this->children; + } + + /** + * @param array $children + */ + public function setChildren(array $children): void + { + foreach ($children as $child){ + $this->children[] = new static($child); + } + } +} \ No newline at end of file diff --git a/builder/Entity/UISettingEntity.php b/builder/Entity/UISettingEntity.php new file mode 100644 index 0000000..454d313 --- /dev/null +++ b/builder/Entity/UISettingEntity.php @@ -0,0 +1,336 @@ +loginBackgroundImage; + } + + /** + * @param mixed $loginBackgroundImage + */ + public function setLoginBackgroundImage($loginBackgroundImage): void + { + $this->loginBackgroundImage = $loginBackgroundImage; + } + + /** + * 版权信息. + * @var string + */ + public $copyright = 'Copyright © 2023-2050 TkView'; + + /** + * 底部链接. + * @var array + */ + public $footerLinks = []; + + /** + * 多标签. + * @var bool + */ + public $uniqueOpened = false; + + /** + * 当前登录用户信息. + * @var UserEntity + */ + public $user = []; + + /** + * 授权配置 + * @var array + */ + public $auth = []; + /** + * 默认表单用户名密码 + * @var array + */ + public $autoUser = []; + + /** + * 菜单数据. + * @var MenuEntity + */ + public $menu = []; + + /** + * 菜单数据. + * @var array + */ + public $menuList = []; + + /** + * 头部URL链接列表. + * @var array[] + */ + public $url = []; + + /** + * 默认首页. + * @var string + */ + public $homeUrl = 'index'; + + /** + * API根地址 + * @var string + */ + public $apiRoot = ''; + + public function isLocal(): bool + { + return $this->isLocal; + } + + public function setIsLocal(bool $isLocal): void + { + $this->isLocal = $isLocal; + } + + public function isLogoShow(): bool + { + return $this->logoShow; + } + + public function setLogoShow(bool $logoShow): void + { + $this->logoShow = $logoShow; + } + + public function getLogo(): string + { + return $this->logo; + } + + public function setLogo($logo): void + { + $this->logo = $logo; + } + + public function getLogoLight() + { + return $this->logoLight; + } + + public function setLogoLight( $logoLight): void + { + $this->logoLight = $logoLight; + } + + public function getLogoMini() + { + return $this->logoMini; + } + + public function setLogoMini($logoMini): void + { + $this->logoMini = $logoMini; + } + + public function getLogoMiniLight() + { + return $this->logoMiniLight; + } + + public function setLogoMiniLight( $logoMiniLight): void + { + $this->logoMiniLight = $logoMiniLight; + } + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getCopyright(): string + { + return $this->copyright; + } + + public function setCopyright(string $copyright): void + { + $this->copyright = $copyright; + } + + public function getFooterLinks(): array + { + return $this->footerLinks; + } + + public function setFooterLinks(array $footerLinks): void + { + $this->footerLinks = $footerLinks; + } + + public function setUser(UserEntity $user): UISettingEntity + { + $this->user = $user; + return $this; + } + + /** + * @return array + */ + public function getMenu(): MenuEntity + { + return $this->menu; + } + + public function setMenu(MenuEntity $menu): void + { + $this->menu = $menu->toArray(); + $this->menuList = $menu->toMenuList(); + } + + public function getMenuList(): array + { + return $this->menuList; + } + + public function isUniqueOpened(): bool + { + return $this->uniqueOpened; + } + + public function setUniqueOpened(bool $uniqueOpened): void + { + $this->uniqueOpened = $uniqueOpened; + } + + public function setUrl(array $url): UISettingEntity + { + $this->url = $url; + return $this; + } + + public function getApiRoot(): string + { + return $this->apiRoot; + } + + public function setApiRoot(string $apiRoot): void + { + $this->apiRoot = $apiRoot; + } + + public function getUser(): UserEntity + { + return $this->user; + } + + public function getHomeUrl(): string + { + return $this->homeUrl; + } + + public function setHomeUrl(string $homeUrl): void + { + $this->homeUrl = $homeUrl; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function getAuth(): array + { + return $this->auth; + } + + public function setAuth(array $auth): void + { + $this->auth = $auth; + } + + public function getAutoUser(): array + { + return $this->autoUser; + } + + public function setAutoUser(array $autoUser): void + { + $this->autoUser = $autoUser; + } +} diff --git a/builder/Entity/UserEntity.php b/builder/Entity/UserEntity.php new file mode 100644 index 0000000..a66193f --- /dev/null +++ b/builder/Entity/UserEntity.php @@ -0,0 +1,116 @@ +uid; + } + + /** + * @param int $uid + */ + public function setUid(int $uid): void + { + $this->uid = $uid; + } + + /** + * @return string + */ + public function getUsername(): string + { + return $this->username; + } + + /** + * @param string $username + */ + public function setUsername(string $username): void + { + $this->username = $username; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName(string $name): void + { + $this->name = $name; + } + + /** + * @return string + */ + public function getAvatar(): string + { + return $this->avatar; + } + + /** + * @param string $avatar + */ + public function setAvatar(string $avatar): void + { + $this->avatar = $avatar; + } + + /** + * @return string + */ + public function getToken(): string + { + return $this->token; + } + + /** + * @param string $token + */ + public function setToken(string $token): void + { + $this->token = $token; + } + + +} \ No newline at end of file diff --git a/builder/Event/ApiAfter.php b/builder/Event/ApiAfter.php new file mode 100644 index 0000000..d719c7f --- /dev/null +++ b/builder/Event/ApiAfter.php @@ -0,0 +1,37 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Event; + +use Psr\Http\Message\ResponseInterface; + +class ApiAfter +{ + protected ?array $apiData; + + protected ResponseInterface $result; + + public function __construct(?array $apiData, ResponseInterface $result) + { + $this->apiData = $apiData; + $this->result = $result; + } + + public function getApiData(): ?array + { + return $this->apiData; + } + + public function getResult(): ResponseInterface + { + return $this->result; + } +} \ No newline at end of file diff --git a/builder/Event/ApiBefore.php b/builder/Event/ApiBefore.php new file mode 100644 index 0000000..dadb5ec --- /dev/null +++ b/builder/Event/ApiBefore.php @@ -0,0 +1,14 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Event; + +class ApiBefore {} \ No newline at end of file diff --git a/builder/Event/Operation.php b/builder/Event/Operation.php new file mode 100644 index 0000000..15361b5 --- /dev/null +++ b/builder/Event/Operation.php @@ -0,0 +1,32 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Event; + +class Operation +{ + /** + * @var array + */ + protected array $requestInfo; + + + public function __construct(array $requestInfo) + { + $this->requestInfo = $requestInfo; + } + + public function getRequestInfo(): array + { + return $this->requestInfo; + } +} \ No newline at end of file diff --git a/builder/Event/RealDeleteUploadFile.php b/builder/Event/RealDeleteUploadFile.php new file mode 100644 index 0000000..21cb998 --- /dev/null +++ b/builder/Event/RealDeleteUploadFile.php @@ -0,0 +1,63 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Event; + +use App\System\Model\SystemUploadfile; +use League\Flysystem\Filesystem; + +class RealDeleteUploadFile +{ + protected SystemUploadfile $model; + + protected bool $confirm = true; + + protected Filesystem $filesystem; + + public function __construct(SystemUploadfile $model, Filesystem $filesystem) + { + $this->model = $model; + $this->filesystem = $filesystem; + } + + /** + * 获取当前模型实例 + * @return SystemUploadfile + */ + public function getModel(): SystemUploadfile + { + return $this->model; + } + + /** + * 获取文件处理系统 + * @return Filesystem + */ + public function getFilesystem(): Filesystem + { + return $this->filesystem; + } + + /** + * 是否删除 + * @return bool + */ + public function getConfirm(): bool + { + return $this->confirm; + } + + public function setConfirm(bool $confirm): void + { + $this->confirm = $confirm; + } +} \ No newline at end of file diff --git a/builder/Event/UploadAfter.php b/builder/Event/UploadAfter.php new file mode 100644 index 0000000..67dad44 --- /dev/null +++ b/builder/Event/UploadAfter.php @@ -0,0 +1,22 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Event; + +class UploadAfter +{ + public array $fileInfo; + + public function __construct(array $fileInfo) + { + $this->fileInfo = $fileInfo; + } +} \ No newline at end of file diff --git a/builder/Event/UserAdd.php b/builder/Event/UserAdd.php new file mode 100644 index 0000000..6900928 --- /dev/null +++ b/builder/Event/UserAdd.php @@ -0,0 +1,23 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Event; + +class UserAdd +{ + public array $userinfo; + + public function __construct(array $userinfo) + { + $this->userinfo = $userinfo; + } +} \ No newline at end of file diff --git a/builder/Event/UserDelete.php b/builder/Event/UserDelete.php new file mode 100644 index 0000000..fd43071 --- /dev/null +++ b/builder/Event/UserDelete.php @@ -0,0 +1,23 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Event; + +class UserDelete +{ + public array $ids; + + public function __construct(array $ids) + { + $this->ids = $ids; + } +} \ No newline at end of file diff --git a/builder/Event/UserLoginAfter.php b/builder/Event/UserLoginAfter.php new file mode 100644 index 0000000..78e7e22 --- /dev/null +++ b/builder/Event/UserLoginAfter.php @@ -0,0 +1,29 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Event; + +class UserLoginAfter +{ + public array $userinfo; + + public bool $loginStatus = true; + + public string $message; + + public string $token; + + public function __construct(array $userinfo) + { + $this->userinfo = $userinfo; + } +} \ No newline at end of file diff --git a/builder/Event/UserLoginBefore.php b/builder/Event/UserLoginBefore.php new file mode 100644 index 0000000..8a50fd0 --- /dev/null +++ b/builder/Event/UserLoginBefore.php @@ -0,0 +1,23 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Event; + +class UserLoginBefore +{ + public array $inputData; + + public function __construct(array $inputData) + { + $this->inputData = $inputData; + } +} \ No newline at end of file diff --git a/builder/Event/UserLogout.php b/builder/Event/UserLogout.php new file mode 100644 index 0000000..ea8aab8 --- /dev/null +++ b/builder/Event/UserLogout.php @@ -0,0 +1,23 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); +namespace Builder\Event; + +class UserLogout +{ + public array $userinfo; + + public function __construct(array $userinfo) + { + $this->userinfo = $userinfo; + } +} \ No newline at end of file diff --git a/builder/Exception/CaptchaException.php b/builder/Exception/CaptchaException.php new file mode 100644 index 0000000..79e56b8 --- /dev/null +++ b/builder/Exception/CaptchaException.php @@ -0,0 +1,18 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Exception; + +class CaptchaException extends MineException +{ + +} \ No newline at end of file diff --git a/builder/Exception/Handler/AppExceptionHandler.php b/builder/Exception/Handler/AppExceptionHandler.php new file mode 100644 index 0000000..d07ab44 --- /dev/null +++ b/builder/Exception/Handler/AppExceptionHandler.php @@ -0,0 +1,57 @@ +console = console(); + $this->logger = container()->get(LoggerFactory::class)->get('mineAdmin'); + } + + public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface + { + $this->console->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile())); + $this->console->error($throwable->getTraceAsString()); + $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile())); + $format = [ + 'success' => false, + 'code' => 500, + 'message' => $throwable->getMessage() + ]; + return $response->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withStatus(500)->withBody(new SwooleStream(Json::encode($format))); + } + + public function isValid(Throwable $throwable): bool + { + return true; + } +} diff --git a/builder/Exception/Handler/NoPermissionExceptionHandler.php b/builder/Exception/Handler/NoPermissionExceptionHandler.php new file mode 100644 index 0000000..4ce7edc --- /dev/null +++ b/builder/Exception/Handler/NoPermissionExceptionHandler.php @@ -0,0 +1,47 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Exception\Handler; + +use Hyperf\ExceptionHandler\ExceptionHandler; +use Hyperf\HttpMessage\Stream\SwooleStream; +use Hyperf\Utils\Codec\Json; +use Builder\Exception\NoPermissionException; +use Builder\Helper\MineCode; +use Psr\Http\Message\ResponseInterface; +use Throwable; + +/** + * Class TokenExceptionHandler + * @package Builder\Exception\Handler + */ +class NoPermissionExceptionHandler extends ExceptionHandler +{ + public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface + { + $this->stopPropagation(); + $format = [ + 'success' => false, + 'message' => $throwable->getMessage(), + 'code' => MineCode::NO_PERMISSION, + ]; + return $response->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withStatus(403)->withBody(new SwooleStream(Json::encode($format))); + } + + public function isValid(Throwable $throwable): bool + { + return $throwable instanceof NoPermissionException; + } +} diff --git a/builder/Exception/Handler/NormalStatusExceptionHandler.php b/builder/Exception/Handler/NormalStatusExceptionHandler.php new file mode 100644 index 0000000..87a360a --- /dev/null +++ b/builder/Exception/Handler/NormalStatusExceptionHandler.php @@ -0,0 +1,54 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Exception\Handler; + +use Hyperf\ExceptionHandler\ExceptionHandler; +use Hyperf\HttpMessage\Stream\SwooleStream; +use Hyperf\Utils\Codec\Json; +use Builder\Exception\NormalStatusException; +use Psr\Http\Message\ResponseInterface; +use Throwable; + +/** + * Class DataNotFoundExceptionHandler + * @package Builder\Exception\Handler + */ +class NormalStatusExceptionHandler extends ExceptionHandler +{ + /** + * @param Throwable $throwable + * @param ResponseInterface $response + * @return ResponseInterface + */ + public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface + { + $this->stopPropagation(); + $format = [ + 'success' => false, + 'message' => $throwable->getMessage(), + ]; + if ($throwable->getCode() != 200 && $throwable->getCode() != 0) { + $format['code'] = $throwable->getCode(); + } +// logger('Exception log')->debug($throwable->getMessage()); + return $response->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withBody(new SwooleStream(Json::encode($format))); + } + + public function isValid(Throwable $throwable): bool + { + return $throwable instanceof NormalStatusException; + } +} diff --git a/builder/Exception/Handler/TokenExceptionHandler.php b/builder/Exception/Handler/TokenExceptionHandler.php new file mode 100644 index 0000000..dbaf5f2 --- /dev/null +++ b/builder/Exception/Handler/TokenExceptionHandler.php @@ -0,0 +1,47 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Exception\Handler; + +use Hyperf\ExceptionHandler\ExceptionHandler; +use Hyperf\HttpMessage\Stream\SwooleStream; +use Hyperf\Utils\Codec\Json; +use Builder\Exception\TokenException; +use Builder\Helper\MineCode; +use Psr\Http\Message\ResponseInterface; +use Throwable; + +/** + * Class TokenExceptionHandler + * @package Builder\Exception\Handler + */ +class TokenExceptionHandler extends ExceptionHandler +{ + public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface + { + $this->stopPropagation(); + $format = [ + 'success' => false, + 'message' => $throwable->getMessage(), + 'code' => MineCode::TOKEN_EXPIRED, + ]; + return $response->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withStatus(401)->withBody(new SwooleStream(Json::encode($format))); + } + + public function isValid(Throwable $throwable): bool + { + return $throwable instanceof TokenException; + } +} diff --git a/builder/Exception/Handler/ValidationExceptionHandler.php b/builder/Exception/Handler/ValidationExceptionHandler.php new file mode 100644 index 0000000..641d338 --- /dev/null +++ b/builder/Exception/Handler/ValidationExceptionHandler.php @@ -0,0 +1,45 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Exception\Handler; + +use Hyperf\ExceptionHandler\ExceptionHandler; +use Hyperf\HttpMessage\Stream\SwooleStream; +use Hyperf\Utils\Codec\Json; +use Hyperf\Validation\ValidationException; +use Builder\Helper\MineCode; +use Psr\Http\Message\ResponseInterface; +use Throwable; + +class ValidationExceptionHandler extends ExceptionHandler +{ + public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface + { + $this->stopPropagation(); + /** @var \Hyperf\Validation\ValidationException $throwable */ + $body = $throwable->validator->errors()->first(); + $format = [ + 'success' => false, + 'message' => $body, + 'code' => MineCode::VALIDATE_FAILED, + ]; + return $response->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withStatus(200)->withBody(new SwooleStream(Json::encode($format))); + } + + public function isValid(Throwable $throwable): bool + { + return $throwable instanceof ValidationException; + } +} \ No newline at end of file diff --git a/builder/Exception/MineException.php b/builder/Exception/MineException.php new file mode 100644 index 0000000..1103fd5 --- /dev/null +++ b/builder/Exception/MineException.php @@ -0,0 +1,18 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Exception; + +class MineException extends \RuntimeException +{ + +} \ No newline at end of file diff --git a/builder/Exception/NoPermissionException.php b/builder/Exception/NoPermissionException.php new file mode 100644 index 0000000..b6f05f3 --- /dev/null +++ b/builder/Exception/NoPermissionException.php @@ -0,0 +1,17 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Exception; + +class NoPermissionException extends MineException +{ +} \ No newline at end of file diff --git a/builder/Exception/NormalStatusException.php b/builder/Exception/NormalStatusException.php new file mode 100644 index 0000000..634d401 --- /dev/null +++ b/builder/Exception/NormalStatusException.php @@ -0,0 +1,17 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Exception; + +class NormalStatusException extends MineException +{ +} \ No newline at end of file diff --git a/builder/Exception/TokenException.php b/builder/Exception/TokenException.php new file mode 100644 index 0000000..dc6b581 --- /dev/null +++ b/builder/Exception/TokenException.php @@ -0,0 +1,17 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Exception; + +class TokenException extends MineException +{ +} \ No newline at end of file diff --git a/builder/Exception/UserBanException.php b/builder/Exception/UserBanException.php new file mode 100644 index 0000000..e6c8fba --- /dev/null +++ b/builder/Exception/UserBanException.php @@ -0,0 +1,17 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Exception; + +class UserBanException extends MineException +{ +} \ No newline at end of file diff --git a/builder/Generator/ApiGenerator.php b/builder/Generator/ApiGenerator.php new file mode 100644 index 0000000..630da15 --- /dev/null +++ b/builder/Generator/ApiGenerator.php @@ -0,0 +1,220 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateTables; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; + +/** + * JS API文件生成 + * Class ApiGenerator + * @package Builder\Generator + */ +class ApiGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return ApiGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): ApiGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $filename = Str::camel(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + $module = Str::lower($this->model->module_name); + $this->filesystem->makeDirectory(BASE_PATH . "/runtime/generate/vue/src/api/{$module}", 0755, true, true); + $path = BASE_PATH . "/runtime/generate/vue/src/api/{$module}/{$filename}.js"; + $this->filesystem->put($path, $this->replace()->getCodeContent()); + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().'/Api/main.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): ApiGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{LOAD_API}', + '{COMMENT}', + '{BUSINESS_NAME}', + '{REQUEST_ROUTE}', + ]; + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->getLoadApi(), + $this->getComment(), + $this->getBusinessName(), + $this->getRequestRoute(), + ]; + } + + protected function getLoadApi(): string + { + $menus = $this->model->generate_menus ? explode(',', $this->model->generate_menus) : []; + $ignoreMenus = ['realDelete', 'recovery']; + + array_unshift($menus, $this->model->type === 'single' ? 'singleList' : 'treeList'); + + foreach ($ignoreMenus as $menu) { + if (in_array($menu, $menus)) { + unset($menus[array_search($menu, $menus)]); + } + } + + $jsCode = ''; + $path = $this->getStubDir() . '/Api/'; + foreach ($menus as $menu) { + $content = $this->filesystem->sharedGet($path . $menu . '.stub'); + $jsCode .= $content; + } + + return $jsCode; + } + + /** + * 获取控制器注释 + * @return string + */ + protected function getComment(): string + { + return $this->getBusinessName(). ' API JS'; + } + + /** + * 获取请求路由 + * @return string + */ + protected function getRequestRoute(): string + { + return Str::lower($this->model->module_name) . '/' . $this->getShortBusinessName(); + } + + /** + * 获取业务名称 + * @return string + */ + protected function getBusinessName(): string + { + return $this->model->menu_name; + } + + /** + * 获取短业务名称 + * @return string + */ + public function getShortBusinessName(): string + { + return Str::camel(str_replace( + Str::lower($this->model->module_name), + '', + str_replace(env('DB_PREFIX'), '', $this->model->table_name) + )); + } + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Generator/CodeGenerator.php b/builder/Generator/CodeGenerator.php new file mode 100644 index 0000000..c21fbd6 --- /dev/null +++ b/builder/Generator/CodeGenerator.php @@ -0,0 +1,20 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +interface CodeGenerator +{ + public function generator(); + + public function preview(); +} \ No newline at end of file diff --git a/builder/Generator/ControllerGenerator.php b/builder/Generator/ControllerGenerator.php new file mode 100644 index 0000000..7e1a8fa --- /dev/null +++ b/builder/Generator/ControllerGenerator.php @@ -0,0 +1,416 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateColumns; +use App\Setting\Model\SettingGenerateTables; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; + +/** + * 控制器生成 + * Class ControllerGenerator + * @package Builder\Generator + */ +class ControllerGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return ControllerGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): ControllerGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->setNamespace($this->model->namespace); + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $module = Str::title($this->model->module_name[0]) . mb_substr($this->model->module_name, 1); + if ($this->model->generate_type === 1) { + $path = BASE_PATH . "/runtime/generate/php/app/{$module}/Controller/"; + } else { + $path = BASE_PATH . "/app/{$module}/Controller/"; + } + if (!empty($this->model->package_name)) { + $path .= Str::title($this->model->package_name) . '/'; + } + $this->filesystem->exists($path) || $this->filesystem->makeDirectory($path, 0755, true, true); + $this->filesystem->put($path . "{$this->getClassName()}.php", $this->replace()->getCodeContent()); + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取生成控制器的类型 + * @return string + */ + public function getType(): string + { + return ucfirst($this->model->type); + } + + /** + * 获取控制器模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir() . 'Controller/main.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): ControllerGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{NAMESPACE}', + '{COMMENT}', + '{USE}', + '{CLASS_NAME}', + '{SERVICE}', + '{CONTROLLER_ROUTE}', + '{FUNCTIONS}', + '{REQUEST}', + '{INDEX_PERMISSION}', + '{RECYCLE_PERMISSION}', + '{SAVE_PERMISSION}', + '{READ_PERMISSION}', + '{UPDATE_PERMISSION}', + '{DELETE_PERMISSION}', + '{REAL_DELETE_PERMISSION}', + '{RECOVERY_PERMISSION}', + '{IMPORT_PERMISSION}', + '{EXPORT_PERMISSION}', + '{DTO_CLASS}', + '{PK}', + '{STATUS_VALUE}', + '{STATUS_FIELD}', + '{NUMBER_FIELD}', + '{NUMBER_TYPE}', + '{NUMBER_VALUE}', + ]; + + + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->initNamespace(), + $this->getComment(), + $this->getUse(), + $this->getClassName(), + $this->getServiceName(), + $this->getControllerRoute(), + $this->getFunctions(), + $this->getRequestName(), + sprintf('%s, %s', Str::lower($this->model->module_name).':'.$this->getShortBusinessName(), $this->getMethodRoute('index')), + $this->getMethodRoute('recycle'), + $this->getMethodRoute('save'), + $this->getMethodRoute('read'), + $this->getMethodRoute('update'), + $this->getMethodRoute('delete'), + $this->getMethodRoute('realDelete'), + $this->getMethodRoute('recovery'), + $this->getMethodRoute('import'), + $this->getMethodRoute('export'), + $this->getDtoClass(), + $this->getPk(), + $this->getStatusValue(), + $this->getStatusField(), + $this->getNumberField(), + $this->getNumberType(), + $this->getNumberValue(), + ]; + } + + /** + * 初始化控制器命名空间 + * @return string + */ + protected function initNamespace(): string + { + $namespace = $this->getNamespace() . "\\Controller"; + if (!empty($this->model->package_name)) { + return $namespace . "\\" . Str::title($this->model->package_name); + } + return $namespace; + } + + /** + * 获取控制器注释 + * @return string + */ + protected function getComment(): string + { + return $this->model->menu_name . '控制器'; + } + + /** + * 获取使用的类命名空间 + * @return string + */ + protected function getUse(): string + { + return <<getNamespace()}\\Service\\{$this->getBusinessName()}Service; +use {$this->getNamespace()}\\Request\\{$this->getBusinessName()}Request; +UseNamespace; + } + + /** + * 获取控制器类名称 + * @return string + */ + protected function getClassName(): string + { + return $this->getBusinessName() . 'Controller'; + } + + /** + * 获取服务类名称 + * @return string + */ + protected function getServiceName(): string + { + return $this->getBusinessName() . 'Service'; + } + + /** + * 获取控制器路由 + * @return string + */ + protected function getControllerRoute(): string + { + return sprintf( + '%s/%s', + Str::lower($this->model->module_name), + $this->getShortBusinessName() + ); + } + + /** + * @return string + */ + protected function getFunctions(): string + { + $menus = $this->model->generate_menus ? explode(',', $this->model->generate_menus) : []; + $otherMenu = [$this->model->type === 'single' ? 'singleList' : 'treeList']; + if (in_array('recycle', $menus)) { + $otherMenu[] = $this->model->type === 'single' ? 'singleRecycleList' : 'treeRecycleList'; + array_push($otherMenu, ...['realDelete', 'recovery']); + unset($menus[array_search('recycle', $menus)]); + } + array_unshift($menus, ...$otherMenu); + $phpCode = ''; + $path = $this->getStubDir() . 'Controller/'; + foreach ($menus as $menu) { + $content = $this->filesystem->sharedGet($path . $menu . '.stub'); + $phpCode .= $content; + } + return $phpCode; + } + + /** + * 获取方法路由 + * @param string $route + * @return string + */ + protected function getMethodRoute(string $route): string + { + return sprintf( + '%s:%s:%s', + Str::lower($this->model->module_name), + $this->getShortBusinessName(), + $route + ); + } + + /** + * @return string + */ + protected function getDtoClass(): string + { + return sprintf( + "\%s\Dto\%s::class", + $this->model->namespace, + $this->getBusinessName() . 'Dto' + ); + } + + /** + * @return string + */ + protected function getPk(): string + { + return SettingGenerateColumns::query() + ->where('table_id', $this->model->id) + ->where('is_pk', self::YES) + ->value('column_name'); + } + + /** + * @return string + */ + protected function getStatusValue(): string + { + return 'statusValue'; + } + + /** + * @return string + */ + protected function getStatusField(): string + { + return 'statusName'; + } + + /** + * @return string + */ + protected function getNumberField(): string + { + return 'numberName'; + } + + /** + * @return string + */ + protected function getNumberType(): string + { + return 'numberType'; + } + + /** + * @return string + */ + protected function getNumberValue(): string + { + return 'numberValue'; + } + + /** + * 获取验证器 + * @return string + */ + protected function getRequestName(): string + { + return $this->getBusinessName(). 'Request'; + } + + /** + * 获取业务名称 + * @return string + */ + public function getBusinessName(): string + { + return Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + /** + * 获取短业务名称 + * @return string + */ + public function getShortBusinessName(): string + { + return Str::camel(str_replace( + Str::lower($this->model->module_name), + '', + str_replace(env('DB_PREFIX'), '', $this->model->table_name) + )); + } + + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Generator/DtoGenerator.php b/builder/Generator/DtoGenerator.php new file mode 100644 index 0000000..ff9d6cd --- /dev/null +++ b/builder/Generator/DtoGenerator.php @@ -0,0 +1,227 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateColumns; +use App\Setting\Model\SettingGenerateTables; +use Hyperf\Database\Model\Collection; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; + +class DtoGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * @var Collection + */ + protected Collection $columns; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return DtoGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): DtoGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->setNamespace($this->model->namespace); + + $this->columns = SettingGenerateColumns::query() + ->where('table_id', $model->id)->orderByDesc('sort') + ->get([ 'column_name', 'column_comment' ]); + + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $module = Str::title($this->model->module_name[0]) . mb_substr($this->model->module_name, 1); + if ($this->model->generate_type === 1) { + $path = BASE_PATH . "/runtime/generate/php/app/{$module}/Dto/"; + } else { + $path = BASE_PATH . "/app/{$module}/Dto/"; + } + $this->filesystem->exists($path) || $this->filesystem->makeDirectory($path, 0755, true, true); + $this->filesystem->put($path . "{$this->getClassName()}.php", $this->replace()->getCodeContent()); + } + + /** + * @return string + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().'/dto.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): DtoGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{NAMESPACE}', + '{COMMENT}', + '{CLASS_NAME}', + '{LIST}', + ]; + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->initNamespace(), + $this->getComment(), + $this->getClassName(), + $this->getList(), + ]; + } + + /** + * 初始化命名空间 + * @return string + */ + protected function initNamespace(): string + { + return $this->getNamespace() . "\\Dto"; + } + + /** + * 获取控制器注释 + * @return string + */ + protected function getComment(): string + { + return $this->model->menu_name. 'Dto (导入导出)'; + } + + /** + * 获取类名称 + * @return string + */ + protected function getClassName(): string + { + return $this->getBusinessName().'Dto'; + } + + /** + * @return string + */ + protected function getList(): string + { + $phpCode = ''; + foreach ($this->columns as $index => $column) { + $phpCode .= str_replace( + ['NAME', 'INDEX', 'FIELD'], + [$column['column_comment'] ?: $column['column_name'], $index, $column['column_name']], + $this->getCodeTemplate() + ); + } + return $phpCode; + } + + protected function getCodeTemplate(): string + { + return sprintf( + " %s\n %s\n\n", + '#[ExcelProperty(value: "NAME", index: INDEX)]', + 'public string $FIELD;' + ); + } + + /** + * 获取业务名称 + * @return string + */ + public function getBusinessName(): string + { + return Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } +} \ No newline at end of file diff --git a/builder/Generator/MapperGenerator.php b/builder/Generator/MapperGenerator.php new file mode 100644 index 0000000..9cee8b4 --- /dev/null +++ b/builder/Generator/MapperGenerator.php @@ -0,0 +1,311 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateColumns; +use App\Setting\Model\SettingGenerateTables; +use App\Setting\Service\SettingGenerateColumnsService; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Generator\Traits\MapperGeneratorTraits; +use Builder\Helper\Str; + +/** + * Mapper类生成 + * Class MapperGenerator + * @package Builder\Generator + */ +class MapperGenerator extends MineGenerator implements CodeGenerator +{ + use MapperGeneratorTraits; + + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return MapperGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): MapperGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->setNamespace($this->model->namespace); + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $module = Str::title($this->model->module_name[0]) . mb_substr($this->model->module_name, 1); + if ($this->model->generate_type === 1) { + $path = BASE_PATH . "/runtime/generate/php/app/{$module}/Mapper/"; + } else { + $path = BASE_PATH . "/app/{$module}/Mapper/"; + } + $this->filesystem->exists($path) || $this->filesystem->makeDirectory($path, 0755, true, true); + $this->filesystem->put($path . "{$this->getClassName()}.php", $this->replace()->getCodeContent()); + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取生成的类型 + * @return string + */ + public function getType(): string + { + return ucfirst($this->model->type); + } + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().$this->getType().'/mapper.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): MapperGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{NAMESPACE}', + '{USE}', + '{COMMENT}', + '{CLASS_NAME}', + '{MODEL}', + '{FIELD_ID}', + '{FIELD_PID}', + '{FIELD_NAME}', + '{SEARCH}' + ]; + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->initNamespace(), + $this->getUse(), + $this->getComment(), + $this->getClassName(), + $this->getModelName(), + $this->getFieldIdName(), + $this->getFieldPidName(), + $this->getFieldName(), + $this->getSearch() + ]; + } + + /** + * 初始化服务类命名空间 + * @return string + */ + protected function initNamespace(): string + { + return $this->getNamespace() . "\\Mapper"; + } + + /** + * 获取控制器注释 + * @return string + */ + protected function getComment(): string + { + return $this->model->menu_name. 'Mapper类'; + } + + /** + * 获取使用的类命名空间 + * @return string + */ + protected function getUse(): string + { + return <<getNamespace()}\\Model\\{$this->getBusinessName()}; +UseNamespace; + } + + /** + * 获取类名称 + * @return string + */ + protected function getClassName(): string + { + return $this->getBusinessName().'Mapper'; + } + + /** + * 获取Model类名称 + * @return string + */ + protected function getModelName(): string + { + return $this->getBusinessName(); + } + + /** + * 获取树表ID + * @return string + */ + protected function getFieldIdName(): string + { + if ($this->getType() == 'Tree') { + if ( $this->model->options['tree_id'] ?? false ) { + return $this->model->options['tree_id']; + } else { + return 'id'; + } + } + return ''; + } + + /** + * 获取树表父ID + * @return string + */ + protected function getFieldPidName(): string + { + if ($this->getType() == 'Tree') { + if ( $this->model->options['tree_pid'] ?? false ) { + return $this->model->options['tree_pid']; + } else { + return 'parent_id'; + } + } + return ''; + } + + /** + * 获取树表显示名称 + * @return string + */ + protected function getFieldName(): string + { + if ($this->getType() == 'Tree') { + if ( $this->model->options['tree_name'] ?? false ) { + return $this->model->options['tree_name']; + } else { + return 'name'; + } + } + return ''; + } + + /** + * 获取搜索内容 + * @return string + */ + protected function getSearch(): string + { + /* @var SettingGenerateColumns $model */ + $model = make(SettingGenerateColumnsService::class)->mapper->getModel(); + $columns = $model->newQuery() + ->where('table_id', $this->model->id) + ->where('is_query', self::YES) + ->get(['column_name', 'column_comment', 'query_type'])->toArray(); + + $phpContent = ''; + foreach ($columns as $column) { + $phpContent .= $this->getSearchCode($column); + } + + return $phpContent; + } + + /** + * 获取业务名称 + * @return string + */ + public function getBusinessName(): string + { + return Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Generator/MineGenerator.php b/builder/Generator/MineGenerator.php new file mode 100644 index 0000000..f55f905 --- /dev/null +++ b/builder/Generator/MineGenerator.php @@ -0,0 +1,77 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use Psr\Container\ContainerInterface; + +abstract class MineGenerator +{ + /** + * @var string + */ + protected string $stubDir; + + /** + * @var string + */ + protected string $namespace; + + /** + * @var ContainerInterface + */ + protected ContainerInterface $container; + + public const NO = 1; + public const YES = 2; + + /** + * MineGenerator constructor. + * @param ContainerInterface $container + */ + public function __construct(ContainerInterface $container) + { + $this->setStubDir(BASE_PATH . '/mine/Generator/Stubs/'); + $this->container = $container; + } + + public function getStubDir(): string + { + return $this->stubDir; + } + + public function setStubDir(string $stubDir) + { + $this->stubDir = $stubDir; + } + + /** + * @return string + */ + public function getNamespace(): string + { + return $this->namespace; + } + + /** + * @param mixed $namespace + */ + public function setNamespace(string $namespace): void + { + $this->namespace = $namespace; + } + + public function replace(): self + { + return $this; + } +} \ No newline at end of file diff --git a/builder/Generator/ModelGenerator.php b/builder/Generator/ModelGenerator.php new file mode 100644 index 0000000..0fecf6a --- /dev/null +++ b/builder/Generator/ModelGenerator.php @@ -0,0 +1,304 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateTables; +use App\Setting\Service\SettingGenerateColumnsService; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; + +/** + * 模型生成 + * Class ModelGenerator + * @package Builder\Generator + */ +class ModelGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return ModelGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): ModelGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->setNamespace($this->model->namespace); + return $this; + } + + /** + * 生成代码 + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function generator(): void + { + $module = Str::title($this->model->module_name[0]) . mb_substr($this->model->module_name, 1); + if ($this->model->generate_type === 1) { + $path = BASE_PATH . "/runtime/generate/php/app/{$module}/Model/"; + } else { + $path = BASE_PATH . "/app/{$module}/Model/"; + } + $this->filesystem->exists($path) || $this->filesystem->makeDirectory($path, 0755, true, true); + + $command = [ + 'command' => 'mine:model-gen', + '--module' => $this->model->module_name, + '--table' => $this->model->table_name + ]; + + if (! Str::contains($this->model->table_name, Str::lower($this->model->module_name))) { + throw new NormalStatusException(t('setting.gen_model_error'), 500); + } + + if (mb_strlen($this->model->table_name) === mb_strlen($this->model->module_name)) { + throw new NormalStatusException(t('setting.gen_model_error'), 500); + } + + $input = new ArrayInput($command); + $output = new NullOutput(); + + /** @var \Symfony\Component\Console\Application $application */ + $application = $this->container->get(\Hyperf\Contract\ApplicationInterface::class); + $application->setAutoExit(false); + + $modelName = Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + + if ($application->run($input, $output) === 0) { + + // 对模型文件处理 + if ($modelName[strlen($modelName) - 1] == 's' && $modelName[strlen($modelName) - 2] != 's') { + $oldName = Str::substr($modelName, 0, (strlen($modelName) - 1)); + $oldPath = BASE_PATH . "/app/{$module}/Model/{$oldName}.php"; + $sourcePath = BASE_PATH . "/app/{$module}/Model/{$modelName}.php"; + $this->filesystem->put( + $sourcePath, + str_replace($oldName, $modelName, $this->filesystem->sharedGet($oldPath)) + ); + @unlink($oldPath); + } else { + $sourcePath = BASE_PATH . "/app/{$module}/Model/{$modelName}.php"; + } + + if (!empty($this->model->options['relations'])) { + $this->filesystem->put( + $sourcePath, + preg_replace('/}$/', $this->getRelations() . "}", $this->filesystem->sharedGet($sourcePath)) + ); + } + + // 压缩包下载 + if ($this->model->generate_type === 1) { + $toPath = BASE_PATH . "/runtime/generate/php/app/{$module}/Model/{$modelName}.php"; + + $isFile = is_file($sourcePath); + + if ($isFile) { + $this->filesystem->copy($sourcePath, $toPath); + } else { + $this->filesystem->move($sourcePath, $toPath); + } + } + } else { + throw new NormalStatusException(t('setting.gen_model_error'), 500); + } + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->placeholderReplace()->getCodeContent(); + } + + /** + * 获取控制器模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().'model.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): ModelGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate(), + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{NAMESPACE}', + '{CLASS_NAME}', + '{TABLE_NAME}', + '{FILL_ABLE}', + '{RELATIONS}', + ]; + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->initNamespace(), + $this->getClassName(), + $this->getTableName(), + $this->getFillAble(), + $this->getRelations(), + ]; + } + + /** + * 初始化模型命名空间 + * @return string + */ + protected function initNamespace(): string + { + return $this->getNamespace() . "\\Model"; + } + + /** + * 获取类名称 + * @return string + */ + protected function getClassName(): string + { + return $this->getBusinessName(); + } + + /** + * 获取表名称 + * @return string + */ + protected function getTableName(): string + { + return $this->model->table_name; + } + + /** + * 获取file able + */ + protected function getFillAble(): string + { + $data = make(SettingGenerateColumnsService::class)->getList( + ['select' => 'column_name', 'table_id' => $this->model->id] + ); + $columns = []; + foreach ($data as $column) { + $columns[] = "'".$column['column_name']."'"; + } + + return implode(', ', $columns); + } + + /** + * @return string + */ + protected function getRelations(): string + { + $prefix = env('DB_PREFIX'); + if (!empty($this->model->options['relations'])) { + $path = $this->getStubDir() . 'ModelRelation/'; + $phpCode = ''; + foreach ($this->model->options['relations'] as $relation) { + $content = $this->filesystem->sharedGet($path . $relation['type'] . '.stub'); + $content = str_replace( + [ '{RELATION_NAME}', '{MODEL_NAME}', '{TABLE_NAME}', '{FOREIGN_KEY}', '{LOCAL_KEY}' ], + [ $relation['name'], $relation['model'], str_replace($prefix, '', $relation['table']), $relation['foreignKey'], $relation['localKey'] ], + $content + ); + $phpCode .= $content; + } + return $phpCode; + } + return ''; + } + + /** + * 获取业务名称 + * @return string + */ + public function getBusinessName(): string + { + return Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } +} \ No newline at end of file diff --git a/builder/Generator/ModuleGenerator.php b/builder/Generator/ModuleGenerator.php new file mode 100644 index 0000000..d17a785 --- /dev/null +++ b/builder/Generator/ModuleGenerator.php @@ -0,0 +1,108 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Generator; + +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Mine; + +class ModuleGenerator extends MineGenerator +{ + /** + * @var array + */ + protected array $moduleInfo; + + /** + * 设置模块信息 + * @param array $moduleInfo + * @return $this + */ + public function setModuleInfo(array $moduleInfo): ModuleGenerator + { + $this->moduleInfo = $moduleInfo; + return $this; + } + + /** + * 生成模块基础架构 + */ + public function createModule(): bool + { + if (! ($this->moduleInfo['name'] ?? false)) { + throw new \RuntimeException('模块名称为空'); + } + + $this->moduleInfo['name'] = ucfirst($this->moduleInfo['name']); + + $mine = new Mine; + $mine->scanModule(); + + if (! empty($mine->getModuleInfo($this->moduleInfo['name']))) { + throw new \RuntimeException('同名模块已存在'); + } + + $appPath = BASE_PATH . '/app/'; + $modulePath = $appPath . $this->moduleInfo['name'] . '/'; + + /** @var Filesystem $filesystem */ + $filesystem = make(Filesystem::class); + $filesystem->makeDirectory($appPath . $this->moduleInfo['name']); + + foreach ($this->getGeneratorDirs() as $dir) { + $filesystem->makeDirectory($modulePath . $dir); + } + + $this->createConfigJson($filesystem); + + return true; + } + + /** + * 创建模块JSON文件 + */ + protected function createConfigJson(Filesystem $filesystem) + { + $json = $filesystem->sharedGet($this->getStubDir() . 'config.stub'); + + $content = str_replace( + ['{NAME}','{LABEL}','{DESCRIPTION}', '{VERSION}'], + [ + $this->moduleInfo['name'], + $this->moduleInfo['label'], + $this->moduleInfo['description'], + $this->moduleInfo['version'] + ], + $json + ); + + $filesystem->put(BASE_PATH . '/app/' .$this->moduleInfo['name'] . '/config.json', $content); + } + + /** + * 生成的目录列表 + */ + protected function getGeneratorDirs(): array + { + return [ + 'Controller', + 'Model', + 'Listener', + 'Request', + 'Service', + 'Mapper', + 'Database', + 'Middleware', + ]; + } +} \ No newline at end of file diff --git a/builder/Generator/RequestGenerator.php b/builder/Generator/RequestGenerator.php new file mode 100644 index 0000000..0b89899 --- /dev/null +++ b/builder/Generator/RequestGenerator.php @@ -0,0 +1,287 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateColumns; +use App\Setting\Model\SettingGenerateTables; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; + +/** + * 验证器生成 + * Class RequestGenerator + * @package Builder\Generator + */ +class RequestGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * @var array + */ + protected array $columns; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return RequestGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): RequestGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->setNamespace($this->model->namespace); + + $this->columns = SettingGenerateColumns::query() + ->where('table_id', $model->id) + ->where(function($query) { + $query->where('is_required', self::YES); + }) + ->orderByDesc('sort') + ->get([ 'column_name', 'column_comment', 'is_insert', 'is_edit' ])->toArray(); + + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $module = Str::title($this->model->module_name[0]) . mb_substr($this->model->module_name, 1); + if ($this->model->generate_type === 1) { + $path = BASE_PATH . "/runtime/generate/php/app/{$module}/Request/"; + } else { + $path = BASE_PATH . "/app/{$module}/Request/"; + } + $this->filesystem->exists($path) || $this->filesystem->makeDirectory($path, 0755, true, true); + $this->filesystem->put($path . "{$this->getClassName()}.php", $this->replace()->getCodeContent()); + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir() . '/Request/main.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): RequestGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{NAMESPACE}', + '{COMMENT}', + '{CLASS_NAME}', + '{RULES}', + '{ATTRIBUTES}', + ]; + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->initNamespace(), + $this->getComment(), + $this->getClassName(), + $this->getRules(), + $this->getAttributes(), + ]; + } + + /** + * 初始化命名空间 + * @return string + */ + protected function initNamespace(): string + { + return $this->getNamespace() . "\\Request"; + } + + /** + * 获取注释 + * @return string + */ + protected function getComment(): string + { + return $this->model->menu_name . '验证数据类'; + } + + /** + * 获取类名称 + * @return string + */ + protected function getClassName(): string + { + return $this->getBusinessName() . 'Request'; + } + + /** + * 获取验证数据规则 + * @return string + */ + protected function getRules(): string + { + $phpContent = ''; + $createCode = ''; + $updateCode = ''; + $path = $this->getStubDir() . '/Request/rule.stub'; + + foreach ($this->columns as $column) { + if ($column['is_insert'] == self::YES) { + $createCode .= $this->getRuleCode($column); + } + if ($column['is_edit'] == self::YES) { + $updateCode .= $this->getRuleCode($column); + } + } + + $phpContent .= str_replace( + ['{METHOD_COMMENT}', '{METHOD_NAME}', '{LIST}'], + ['新增数据验证规则', 'saveRules', $createCode], + $this->filesystem->sharedGet($path) + ); + $phpContent .= str_replace( + ['{METHOD_COMMENT}', '{METHOD_NAME}', '{LIST}'], + ['更新数据验证规则', 'updateRules', $updateCode], + $this->filesystem->sharedGet($path) + ); + + return $phpContent; + } + + /** + * @param array $column + * @return string + */ + protected function getRuleCode(array &$column): string + { + $space = ' '; + return sprintf( + "%s//%s 验证\n%s'%s' => 'required',\n", + $space, $column['column_comment'], + $space, $column['column_name'] + ); + } + + /** + * @return string + */ + protected function getAttributes(): string + { + $phpCode = ''; + $path = $this->getStubDir() . '/Request/attribute.stub'; + foreach ($this->columns as $column) { + $phpCode .= $this->getAttributeCode($column); + } + return str_replace('{LIST}', $phpCode, $this->filesystem->sharedGet($path)); + } + + /** + * @param array $column + * @return string + */ + protected function getAttributeCode(array &$column): string + { + $space = ' '; + return sprintf( + "%s'%s' => '%s',\n", $space, $column['column_name'], $column['column_comment'] + ); + } + + /** + * 获取业务名称 + * @return string + */ + public function getBusinessName(): string + { + return Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Generator/ServiceGenerator.php b/builder/Generator/ServiceGenerator.php new file mode 100644 index 0000000..1dfdd05 --- /dev/null +++ b/builder/Generator/ServiceGenerator.php @@ -0,0 +1,263 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateTables; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; + +/** + * 服务类生成 + * Class ServiceGenerator + * @package Builder\Generator + */ +class ServiceGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return ServiceGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): ServiceGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->setNamespace($this->model->namespace); + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $module = Str::title($this->model->module_name[0]) . mb_substr($this->model->module_name, 1); + if ($this->model->generate_type === 1) { + $path = BASE_PATH . "/runtime/generate/php/app/{$module}/Service/"; + } else { + $path = BASE_PATH . "/app/{$module}/Service/"; + } + $this->filesystem->exists($path) || $this->filesystem->makeDirectory($path, 0755, true, true); + $this->filesystem->put($path . "{$this->getClassName()}.php", $this->replace()->getCodeContent()); + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取生成的类型 + * @return string + */ + public function getType(): string + { + return ucfirst($this->model->type); + } + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().$this->getType().'/service.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): ServiceGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{NAMESPACE}', + '{USE}', + '{COMMENT}', + '{CLASS_NAME}', + '{MAPPER}', + '{FIELD_ID}', + '{FIELD_PID}' + ]; + } + + /** + * 获取要替换占位符的内容 + */ + protected function getReplaceContent(): array + { + return [ + $this->initNamespace(), + $this->getUse(), + $this->getComment(), + $this->getClassName(), + $this->getMapperName(), + $this->getFieldIdName(), + $this->getFieldPidName(), + ]; + } + + /** + * 初始化服务类命名空间 + * @return string + */ + protected function initNamespace(): string + { + return $this->getNamespace() . "\\Service"; + } + + /** + * 获取控制器注释 + * @return string + */ + protected function getComment(): string + { + return $this->model->menu_name. '服务类'; + } + + /** + * 获取使用的类命名空间 + * @return string + */ + protected function getUse(): string + { + return <<getNamespace()}\\Mapper\\{$this->getBusinessName()}Mapper; +UseNamespace; + } + + /** + * 获取控制器类名称 + * @return string + */ + protected function getClassName(): string + { + return $this->getBusinessName().'Service'; + } + + /** + * 获取Mapper类名称 + * @return string + */ + protected function getMapperName(): string + { + return $this->getBusinessName().'Mapper'; + } + + /** + * 获取树表ID + * @return string + */ + protected function getFieldIdName(): string + { + if ($this->getType() == 'Tree') { + if ( $this->model->options['tree_id'] ?? false ) { + return $this->model->options['tree_id']; + } else { + return 'id'; + } + } + return ''; + } + + /** + * 获取树表父ID + * @return string + */ + protected function getFieldPidName(): string + { + if ($this->getType() == 'Tree') { + if ( $this->model->options['tree_pid'] ?? false ) { + return $this->model->options['tree_pid']; + } else { + return 'parent_id'; + } + } + return ''; + } + + /** + * 获取业务名称 + * @return string + */ + public function getBusinessName(): string + { + return Str::studly(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Generator/SqlGenerator.php b/builder/Generator/SqlGenerator.php new file mode 100644 index 0000000..9bfd950 --- /dev/null +++ b/builder/Generator/SqlGenerator.php @@ -0,0 +1,288 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateTables; +use App\System\Model\SystemMenu; +use Hyperf\DbConnection\Db; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +/** + * 菜单SQL文件生成 + * Class SqlGenerator + * @package Builder\Generator + */ +class SqlGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * @var int + */ + protected int $adminId; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @param int $adminId + * @return SqlGenerator + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \Exception + */ + public function setGenInfo(SettingGenerateTables $model, int $adminId): SqlGenerator + { + $this->model = $model; + $this->adminId = $adminId; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + return $this->placeholderReplace(); + } + + /** + * 生成代码 + * @throws \Exception + */ + public function generator(): void + { + $path = BASE_PATH . "/runtime/generate/{$this->getShortBusinessName()}Menu.sql"; + $this->filesystem->makeDirectory(BASE_PATH . "/runtime/generate/", 0755, true, true); + $this->filesystem->put($path, $this->placeholderReplace()->getCodeContent()); + + if ($this->model->build_menu === self::YES) { + Db::connection()->getPdo()->exec( + str_replace(["\r", "\n"], ['', ''], $this->replace()->getCodeContent()) + ); + } + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().'/Sql/main.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + * @throws \Exception + */ + protected function placeholderReplace(): SqlGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{LOAD_MENU}', + '{PARENT_ID}', + '{TABLE_NAME}', + '{LEVEL}', + '{NAME}', + '{CODE}', + '{ROUTE}', + '{VUE_TEMPLATE}', + '{ADMIN_ID}' + ]; + } + + /** + * 获取要替换占位符的内容 + * @throws \Exception + */ + protected function getReplaceContent(): array + { + return [ + $this->getLoadMenu(), + $this->getParentId(), + $this->getTableName(), + $this->getLevel(), + $this->model->menu_name, + $this->getCode(), + $this->getRoute(), + $this->getVueTemplate(), + $this->getAdminId() + ]; + } + + protected function getLoadMenu(): string + { + $menus = $this->model->generate_menus ? explode(',', $this->model->generate_menus) : []; + $ignoreMenus = ['realDelete', 'recovery', 'changeStatus', 'numberOperation']; + + foreach ($ignoreMenus as $menu) { + if (in_array($menu, $menus)) { + unset($menus[array_search($menu, $menus)]); + } + } + + $sql = ''; + $path = $this->getStubDir() . '/Sql/'; + foreach ($menus as $menu) { + $content = $this->filesystem->sharedGet($path . $menu . '.stub'); + $sql .= $content; + } + return $sql; + } + + /** + * 获取菜单父ID + * @return int + */ + protected function getParentId(): int + { + return $this->model->belong_menu_id; + } + + /** + * 获取菜单表表名 + * @return string + */ + protected function getTableName(): string + { + return env('DB_PREFIX') . (SystemMenu::getModel())->getTable(); + } + + /** + * 获取菜单层级value + * @return string + */ + protected function getLevel(): string + { + if ($this->model->belong_menu_id !== 0) { + $model = SystemMenu::find($this->model->belong_menu_id, ['id', 'level']); + return $model->level . ',' . $model->id; + } else { + return '0'; + } + } + + /** + * 获取菜单标识代码 + * @return string + */ + protected function getCode(): string + { + return Str::lower($this->model->module_name) . ':' . $this->getShortBusinessName(); + } + + /** + * 获取vue router地址 + * @return string + */ + protected function getRoute(): string + { + return Str::lower($this->model->module_name) . '/' . $this->getShortBusinessName(); + } + + + /** + * 获取Vue模板路径 + * @return string + */ + protected function getVueTemplate(): string + { + return Str::lower($this->model->module_name) . '/' . $this->getShortBusinessName() . '/index'; + } + + /** + * 获取短业务名称 + * @return string + */ + public function getShortBusinessName(): string + { + return Str::camel(str_replace( + Str::lower($this->model->module_name), + '', + str_replace(env('DB_PREFIX'), '', $this->model->table_name) + )); + } + + /** + * 获取当前登陆人ID + * @return string + */ + protected function getAdminId(): string + { + return (string) $this->adminId; + } + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Api/changeStatus.stub b/builder/Generator/Stubs/Api/changeStatus.stub new file mode 100644 index 0000000..bb8f52e --- /dev/null +++ b/builder/Generator/Stubs/Api/changeStatus.stub @@ -0,0 +1,12 @@ + + /** + * 更改{BUSINESS_NAME}数据 + * @returns + */ + changeStatus (data = {}) { + return request({ + url: '{REQUEST_ROUTE}/changeStatus', + method: 'put', + data + }) + }, diff --git a/builder/Generator/Stubs/Api/delete.stub b/builder/Generator/Stubs/Api/delete.stub new file mode 100644 index 0000000..bbbeb36 --- /dev/null +++ b/builder/Generator/Stubs/Api/delete.stub @@ -0,0 +1,12 @@ + + /** + * 将{BUSINESS_NAME}删除,有软删除则移动到回收站 + * @returns + */ + deletes (data) { + return request({ + url: '{REQUEST_ROUTE}/delete', + method: 'delete', + data + }) + }, diff --git a/builder/Generator/Stubs/Api/export.stub b/builder/Generator/Stubs/Api/export.stub new file mode 100644 index 0000000..693e888 --- /dev/null +++ b/builder/Generator/Stubs/Api/export.stub @@ -0,0 +1,13 @@ + + /** + * {BUSINESS_NAME}导出 + * @returns + */ + exportExcel (params = {}) { + return request({ + url: '{REQUEST_ROUTE}/export', + method: 'post', + responseType: 'blob', + params + }) + }, diff --git a/builder/Generator/Stubs/Api/import.stub b/builder/Generator/Stubs/Api/import.stub new file mode 100644 index 0000000..7d1f10a --- /dev/null +++ b/builder/Generator/Stubs/Api/import.stub @@ -0,0 +1,24 @@ + + /** + * {BUSINESS_NAME}导入 + * @returns + */ + importExcel (data = {}) { + return request({ + url: '{REQUEST_ROUTE}/import', + method: 'post', + data + }) + }, + + /** + * {BUSINESS_NAME}下载模板 + * @returns + */ + downloadTemplate () { + return request({ + url: '{REQUEST_ROUTE}/downloadTemplate', + method: 'post', + responseType: 'blob' + }) + }, diff --git a/builder/Generator/Stubs/Api/main.stub b/builder/Generator/Stubs/Api/main.stub new file mode 100644 index 0000000..8024337 --- /dev/null +++ b/builder/Generator/Stubs/Api/main.stub @@ -0,0 +1,10 @@ +import { request } from '@/utils/request.js' + +/** + * {COMMENT} + */ + +export default { +{LOAD_API} + +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Api/numberOperation.stub b/builder/Generator/Stubs/Api/numberOperation.stub new file mode 100644 index 0000000..21ffadd --- /dev/null +++ b/builder/Generator/Stubs/Api/numberOperation.stub @@ -0,0 +1,12 @@ + + /** + * 修改{BUSINESS_NAME}数值数据,自增自减 + * @returns + */ + numberOperation (data = {}) { + return request({ + url: '{REQUEST_ROUTE}/numberOperation', + method: 'put', + data + }) + }, diff --git a/builder/Generator/Stubs/Api/read.stub b/builder/Generator/Stubs/Api/read.stub new file mode 100644 index 0000000..de34b9c --- /dev/null +++ b/builder/Generator/Stubs/Api/read.stub @@ -0,0 +1,12 @@ + + /** + * 读取{BUSINESS_NAME} + * @returns + */ + read (data = {}) { + return request({ + url: '{REQUEST_ROUTE}/read', + method: 'get', + data + }) + }, diff --git a/builder/Generator/Stubs/Api/recycle.stub b/builder/Generator/Stubs/Api/recycle.stub new file mode 100644 index 0000000..0760103 --- /dev/null +++ b/builder/Generator/Stubs/Api/recycle.stub @@ -0,0 +1,36 @@ + + /** + * 从回收站获取{BUSINESS_NAME}数据列表 + * @returns + */ + getRecycleList (params = {}) { + return request({ + url: '{REQUEST_ROUTE}/recycle', + method: 'get', + params + }) + }, + + /** + * 恢复{BUSINESS_NAME}数据 + * @returns + */ + recoverys (data) { + return request({ + url: '{REQUEST_ROUTE}/recovery', + method: 'put', + data + }) + }, + + /** + * 真实删除{BUSINESS_NAME} + * @returns + */ + realDeletes (data) { + return request({ + url: '{REQUEST_ROUTE}/realDelete', + method: 'delete', + data + }) + }, diff --git a/builder/Generator/Stubs/Api/save.stub b/builder/Generator/Stubs/Api/save.stub new file mode 100644 index 0000000..3253fc8 --- /dev/null +++ b/builder/Generator/Stubs/Api/save.stub @@ -0,0 +1,12 @@ + + /** + * 添加{BUSINESS_NAME} + * @returns + */ + save (data = {}) { + return request({ + url: '{REQUEST_ROUTE}/save', + method: 'post', + data + }) + }, diff --git a/builder/Generator/Stubs/Api/singleList.stub b/builder/Generator/Stubs/Api/singleList.stub new file mode 100644 index 0000000..a2d75d0 --- /dev/null +++ b/builder/Generator/Stubs/Api/singleList.stub @@ -0,0 +1,12 @@ + + /** + * 获取{BUSINESS_NAME}分页列表 + * @returns + */ + getList (params = {}) { + return request({ + url: '{REQUEST_ROUTE}/index', + method: 'get', + params + }) + }, diff --git a/builder/Generator/Stubs/Api/treeList.stub b/builder/Generator/Stubs/Api/treeList.stub new file mode 100644 index 0000000..d6ae33a --- /dev/null +++ b/builder/Generator/Stubs/Api/treeList.stub @@ -0,0 +1,24 @@ + + /** + * 获取{BUSINESS_NAME}列表 + * @returns + */ + getList (params = {}) { + return request({ + url: '{REQUEST_ROUTE}/index', + method: 'get', + params + }) + }, + + /** + * 获取{BUSINESS_NAME}选择树 + * @returns + */ + tree () { + return request({ + url: '{REQUEST_ROUTE}/tree', + method: 'get' + }) + }, + diff --git a/builder/Generator/Stubs/Api/update.stub b/builder/Generator/Stubs/Api/update.stub new file mode 100644 index 0000000..16cd446 --- /dev/null +++ b/builder/Generator/Stubs/Api/update.stub @@ -0,0 +1,12 @@ + + /** + * 更新{BUSINESS_NAME}数据 + * @returns + */ + update (id, data = {}) { + return request({ + url: '{REQUEST_ROUTE}/update/' + id, + method: 'put', + data + }) + }, diff --git a/builder/Generator/Stubs/Controller/changeStatus.stub b/builder/Generator/Stubs/Controller/changeStatus.stub new file mode 100644 index 0000000..8e481d7 --- /dev/null +++ b/builder/Generator/Stubs/Controller/changeStatus.stub @@ -0,0 +1,16 @@ + + /** + * 更改数据状态 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("changeStatus"), Permission("{UPDATE_PERMISSION}"), OperationLog] + public function changeStatus(): ResponseInterface + { + return $this->service->changeStatus( + (int) $this->request->input('{PK}'), + (string) $this->request->input('{STATUS_VALUE}'), + (string) $this->request->input('{STATUS_FIELD}') + ) ? $this->success() : $this->error(); + } diff --git a/builder/Generator/Stubs/Controller/delete.stub b/builder/Generator/Stubs/Controller/delete.stub new file mode 100644 index 0000000..ea80fab --- /dev/null +++ b/builder/Generator/Stubs/Controller/delete.stub @@ -0,0 +1,12 @@ + + /** + * 单个或批量删除数据到回收站 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("delete"), Permission("{DELETE_PERMISSION}"), OperationLog] + public function delete(): ResponseInterface + { + return $this->service->delete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } diff --git a/builder/Generator/Stubs/Controller/export.stub b/builder/Generator/Stubs/Controller/export.stub new file mode 100644 index 0000000..b05c428 --- /dev/null +++ b/builder/Generator/Stubs/Controller/export.stub @@ -0,0 +1,13 @@ + + /** + * 数据导出 + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("export"), Permission("{EXPORT_PERMISSION}"), OperationLog] + public function export(): ResponseInterface + { + return $this->service->export($this->request->all(), {DTO_CLASS}, '导出数据列表'); + } diff --git a/builder/Generator/Stubs/Controller/import.stub b/builder/Generator/Stubs/Controller/import.stub new file mode 100644 index 0000000..244e40b --- /dev/null +++ b/builder/Generator/Stubs/Controller/import.stub @@ -0,0 +1,25 @@ + + /** + * 数据导入 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("import"), Permission("{IMPORT_PERMISSION}")] + public function import(): ResponseInterface + { + return $this->service->import({DTO_CLASS}) ? $this->success() : $this->error(); + } + + /** + * 下载导入模板 + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("downloadTemplate")] + public function downloadTemplate(): ResponseInterface + { + return (new \Builder\MineCollection)->export({DTO_CLASS}, '模板下载', []); + } diff --git a/builder/Generator/Stubs/Controller/main.stub b/builder/Generator/Stubs/Controller/main.stub new file mode 100644 index 0000000..e59a85b --- /dev/null +++ b/builder/Generator/Stubs/Controller/main.stub @@ -0,0 +1,43 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace {NAMESPACE}; + +{USE} +use Hyperf\Di\Annotation\Inject; +use Hyperf\HttpServer\Annotation\Controller; +use Hyperf\HttpServer\Annotation\DeleteMapping; +use Hyperf\HttpServer\Annotation\GetMapping; +use Hyperf\HttpServer\Annotation\PostMapping; +use Hyperf\HttpServer\Annotation\PutMapping; +use Builder\Annotation\Auth; +use Builder\Annotation\OperationLog; +use Builder\Annotation\Permission; +use Builder\MineController; +use Psr\Http\Message\ResponseInterface; + +/** + * {COMMENT} + * Class {CLASS_NAME} + */ +#[Controller(prefix: "{CONTROLLER_ROUTE}"), Auth] +class {CLASS_NAME} extends MineController +{ + /** + * 业务处理服务 + * {SERVICE} + */ + #[Inject] + protected {SERVICE} $service; + + {FUNCTIONS} +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Controller/numberOperation.stub b/builder/Generator/Stubs/Controller/numberOperation.stub new file mode 100644 index 0000000..8f94e14 --- /dev/null +++ b/builder/Generator/Stubs/Controller/numberOperation.stub @@ -0,0 +1,16 @@ + + /** + * 数字运算操作 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("numberOperation"), Permission("{UPDATE_PERMISSION}"), OperationLog] + public function numberOperation(): ResponseInterface + { + return $this->service->numberOperation( + (int) $this->request->input('{PK}'), + (string) $this->request->input('{NUMBER_FIELD}'), + (int) $this->request->input('{NUMBER_VALUE}', 1), + ) ? $this->success() : $this->error(); + } diff --git a/builder/Generator/Stubs/Controller/read.stub b/builder/Generator/Stubs/Controller/read.stub new file mode 100644 index 0000000..81cf81b --- /dev/null +++ b/builder/Generator/Stubs/Controller/read.stub @@ -0,0 +1,13 @@ + + /** + * 读取数据 + * @param int $id + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("read/{id}"), Permission("{READ_PERMISSION}")] + public function read(int $id): ResponseInterface + { + return $this->success($this->service->read($id)); + } diff --git a/builder/Generator/Stubs/Controller/realDelete.stub b/builder/Generator/Stubs/Controller/realDelete.stub new file mode 100644 index 0000000..38b3d50 --- /dev/null +++ b/builder/Generator/Stubs/Controller/realDelete.stub @@ -0,0 +1,12 @@ + + /** + * 单个或批量真实删除数据 (清空回收站) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[DeleteMapping("realDelete"), Permission("{REAL_DELETE_PERMISSION}"), OperationLog] + public function realDelete(): ResponseInterface + { + return $this->service->realDelete((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } diff --git a/builder/Generator/Stubs/Controller/recovery.stub b/builder/Generator/Stubs/Controller/recovery.stub new file mode 100644 index 0000000..4a7f16e --- /dev/null +++ b/builder/Generator/Stubs/Controller/recovery.stub @@ -0,0 +1,12 @@ + + /** + * 单个或批量恢复在回收站的数据 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("recovery"), Permission("{RECOVERY_PERMISSION}"), OperationLog] + public function recovery(): ResponseInterface + { + return $this->service->recovery((array) $this->request->input('ids', [])) ? $this->success() : $this->error(); + } diff --git a/builder/Generator/Stubs/Controller/save.stub b/builder/Generator/Stubs/Controller/save.stub new file mode 100644 index 0000000..8873930 --- /dev/null +++ b/builder/Generator/Stubs/Controller/save.stub @@ -0,0 +1,13 @@ + + /** + * 新增 + * @param {REQUEST} $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PostMapping("save"), Permission("{SAVE_PERMISSION}"), OperationLog] + public function save({REQUEST} $request): ResponseInterface + { + return $this->success(['id' => $this->service->save($request->all())]); + } diff --git a/builder/Generator/Stubs/Controller/singleList.stub b/builder/Generator/Stubs/Controller/singleList.stub new file mode 100644 index 0000000..bfafee8 --- /dev/null +++ b/builder/Generator/Stubs/Controller/singleList.stub @@ -0,0 +1,12 @@ + + /** + * 列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("index"), Permission("{INDEX_PERMISSION}")] + public function index(): ResponseInterface + { + return $this->success($this->service->getPageList($this->request->all())); + } diff --git a/builder/Generator/Stubs/Controller/singleRecycleList.stub b/builder/Generator/Stubs/Controller/singleRecycleList.stub new file mode 100644 index 0000000..3df13e7 --- /dev/null +++ b/builder/Generator/Stubs/Controller/singleRecycleList.stub @@ -0,0 +1,12 @@ + + /** + * 回收站列表 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("{RECYCLE_PERMISSION}")] + public function recycle(): ResponseInterface + { + return $this->success($this->service->getPageListByRecycle($this->request->all())); + } diff --git a/builder/Generator/Stubs/Controller/treeList.stub b/builder/Generator/Stubs/Controller/treeList.stub new file mode 100644 index 0000000..ae7045e --- /dev/null +++ b/builder/Generator/Stubs/Controller/treeList.stub @@ -0,0 +1,24 @@ + + /** + * 获取列表树 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("index"), Permission("{INDEX_PERMISSION}")] + public function index(): ResponseInterface + { + return $this->success($this->service->getTreeList($this->request->all())); + } + + /** + * 前端选择树(不需要权限) + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("tree")] + public function tree(): ResponseInterface + { + return $this->success($this->service->getSelectTree()); + } diff --git a/builder/Generator/Stubs/Controller/treeRecycleList.stub b/builder/Generator/Stubs/Controller/treeRecycleList.stub new file mode 100644 index 0000000..b679600 --- /dev/null +++ b/builder/Generator/Stubs/Controller/treeRecycleList.stub @@ -0,0 +1,12 @@ + + /** + * 回收站列表树 + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[GetMapping("recycle"), Permission("{RECYCLE_PERMISSION}")] + public function recycle():ResponseInterface + { + return $this->success($this->service->getTreeListByRecycle($this->request->all())); + } diff --git a/builder/Generator/Stubs/Controller/update.stub b/builder/Generator/Stubs/Controller/update.stub new file mode 100644 index 0000000..999cb3b --- /dev/null +++ b/builder/Generator/Stubs/Controller/update.stub @@ -0,0 +1,14 @@ + + /** + * 更新 + * @param int $id + * @param {REQUEST} $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[PutMapping("update/{id}"), Permission("{UPDATE_PERMISSION}"), OperationLog] + public function update(int $id, {REQUEST} $request): ResponseInterface + { + return $this->service->update($id, $request->all()) ? $this->success() : $this->error(); + } diff --git a/builder/Generator/Stubs/ModelRelation/belongsTo.stub b/builder/Generator/Stubs/ModelRelation/belongsTo.stub new file mode 100644 index 0000000..656197d --- /dev/null +++ b/builder/Generator/Stubs/ModelRelation/belongsTo.stub @@ -0,0 +1,9 @@ + + /** + * 定义 {RELATION_NAME} 关联 + * @return \Hyperf\Database\Model\Relations\belongsTo + */ + public function {RELATION_NAME}() : \Hyperf\Database\Model\Relations\belongsTo + { + return $this->belongsTo({MODEL_NAME}::class, '{FOREIGN_KEY}', '{LOCAL_KEY}'); + } diff --git a/builder/Generator/Stubs/ModelRelation/belongsToMany.stub b/builder/Generator/Stubs/ModelRelation/belongsToMany.stub new file mode 100644 index 0000000..3fda190 --- /dev/null +++ b/builder/Generator/Stubs/ModelRelation/belongsToMany.stub @@ -0,0 +1,9 @@ + + /** + * 定义 {RELATION_NAME} 关联 + * @return \Hyperf\Database\Model\Relations\belongsToMany + */ + public function {RELATION_NAME}() : \Hyperf\Database\Model\Relations\belongsToMany + { + return $this->belongsToMany({MODEL_NAME}::class, '{TABLE_NAME}', '{FOREIGN_KEY}', '{LOCAL_KEY}'); + } diff --git a/builder/Generator/Stubs/ModelRelation/hasMany.stub b/builder/Generator/Stubs/ModelRelation/hasMany.stub new file mode 100644 index 0000000..ebc8b11 --- /dev/null +++ b/builder/Generator/Stubs/ModelRelation/hasMany.stub @@ -0,0 +1,9 @@ + + /** + * 定义 {RELATION_NAME} 关联 + * @return \Hyperf\Database\Model\Relations\hasMany + */ + public function {RELATION_NAME}() : \Hyperf\Database\Model\Relations\hasMany + { + return $this->hasMany({MODEL_NAME}::class, '{FOREIGN_KEY}', '{LOCAL_KEY}'); + } diff --git a/builder/Generator/Stubs/ModelRelation/hasOne.stub b/builder/Generator/Stubs/ModelRelation/hasOne.stub new file mode 100644 index 0000000..fcb36d2 --- /dev/null +++ b/builder/Generator/Stubs/ModelRelation/hasOne.stub @@ -0,0 +1,9 @@ + + /** + * 定义 {RELATION_NAME} 关联 + * @return \Hyperf\Database\Model\Relations\hasOne + */ + public function {RELATION_NAME}() : \Hyperf\Database\Model\Relations\hasOne + { + return $this->hasOne({MODEL_NAME}::class, '{FOREIGN_KEY}', '{LOCAL_KEY}'); + } diff --git a/builder/Generator/Stubs/Request/attribute.stub b/builder/Generator/Stubs/Request/attribute.stub new file mode 100644 index 0000000..675ab73 --- /dev/null +++ b/builder/Generator/Stubs/Request/attribute.stub @@ -0,0 +1,11 @@ + + /** + * 字段映射名称 + * return array + */ + public function attributes(): array + { + return [ +{LIST} + ]; + } \ No newline at end of file diff --git a/builder/Generator/Stubs/Request/main.stub b/builder/Generator/Stubs/Request/main.stub new file mode 100644 index 0000000..1821db5 --- /dev/null +++ b/builder/Generator/Stubs/Request/main.stub @@ -0,0 +1,33 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ +namespace {NAMESPACE}; + +use Builder\MineFormRequest; + +/** + * {COMMENT} + */ +class {CLASS_NAME} extends MineFormRequest +{ + /** + * 公共规则 + */ + public function commonRules(): array + { + return []; + } + + {RULES} + + {ATTRIBUTES} + +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Request/rule.stub b/builder/Generator/Stubs/Request/rule.stub new file mode 100644 index 0000000..a4d7a6a --- /dev/null +++ b/builder/Generator/Stubs/Request/rule.stub @@ -0,0 +1,11 @@ + + /** + * {METHOD_COMMENT} + * return array + */ + public function {METHOD_NAME}(): array + { + return [ +{LIST} + ]; + } \ No newline at end of file diff --git a/builder/Generator/Stubs/Single/mapper.stub b/builder/Generator/Stubs/Single/mapper.stub new file mode 100644 index 0000000..01a9da6 --- /dev/null +++ b/builder/Generator/Stubs/Single/mapper.stub @@ -0,0 +1,45 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace {NAMESPACE}; + +{USE} +use Hyperf\Database\Model\Builder; +use Builder\Abstracts\AbstractMapper; + +/** + * {COMMENT} + */ +class {CLASS_NAME} extends AbstractMapper +{ + /** + * @var {MODEL} + */ + public $model; + + public function assignModel() + { + $this->model = {MODEL}::class; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + {SEARCH} + return $query; + } +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Single/service.stub b/builder/Generator/Stubs/Single/service.stub new file mode 100644 index 0000000..0073b79 --- /dev/null +++ b/builder/Generator/Stubs/Single/service.stub @@ -0,0 +1,32 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace {NAMESPACE}; + +{USE} +use Builder\Abstracts\AbstractService; + +/** + * {COMMENT} + */ +class {CLASS_NAME} extends AbstractService +{ + /** + * @var {MAPPER} + */ + public $mapper; + + public function __construct({MAPPER} $mapper) + { + $this->mapper = $mapper; + } +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Sql/delete.stub b/builder/Generator/Stubs/Sql/delete.stub new file mode 100644 index 0000000..4928d3e --- /dev/null +++ b/builder/Generator/Stubs/Sql/delete.stub @@ -0,0 +1,2 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '删除'), CONCAT('{CODE}',':delete'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Sql/export.stub b/builder/Generator/Stubs/Sql/export.stub new file mode 100644 index 0000000..8f20b42 --- /dev/null +++ b/builder/Generator/Stubs/Sql/export.stub @@ -0,0 +1,2 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '导出'), CONCAT('{CODE}',':export'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Sql/import.stub b/builder/Generator/Stubs/Sql/import.stub new file mode 100644 index 0000000..60674b2 --- /dev/null +++ b/builder/Generator/Stubs/Sql/import.stub @@ -0,0 +1,2 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '导入'), CONCAT('{CODE}',':import'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Sql/main.stub b/builder/Generator/Stubs/Sql/main.stub new file mode 100644 index 0000000..cbec116 --- /dev/null +++ b/builder/Generator/Stubs/Sql/main.stub @@ -0,0 +1,7 @@ +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES ({PARENT_ID}, '{LEVEL}', '{NAME}', '{CODE}', 'icon-home', '{ROUTE}', '{VUE_TEMPLATE}', NULL, '2', 'M', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); + +SET @id := LAST_INSERT_ID(); +SET @level := CONCAT('{LEVEL}', ',', @id); + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '列表'), CONCAT('{CODE}',':index'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); +{LOAD_MENU} \ No newline at end of file diff --git a/builder/Generator/Stubs/Sql/read.stub b/builder/Generator/Stubs/Sql/read.stub new file mode 100644 index 0000000..4afb26c --- /dev/null +++ b/builder/Generator/Stubs/Sql/read.stub @@ -0,0 +1,2 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '读取'), CONCAT('{CODE}',':read'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Sql/recycle.stub b/builder/Generator/Stubs/Sql/recycle.stub new file mode 100644 index 0000000..c9662d0 --- /dev/null +++ b/builder/Generator/Stubs/Sql/recycle.stub @@ -0,0 +1,6 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '回收站'), CONCAT('{CODE}',':recycle'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '恢复'), CONCAT('{CODE}',':recovery'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '真实删除'), CONCAT('{CODE}',':realDelete'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Sql/save.stub b/builder/Generator/Stubs/Sql/save.stub new file mode 100644 index 0000000..540890f --- /dev/null +++ b/builder/Generator/Stubs/Sql/save.stub @@ -0,0 +1,2 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '保存'), CONCAT('{CODE}',':save'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Sql/update.stub b/builder/Generator/Stubs/Sql/update.stub new file mode 100644 index 0000000..b9741a8 --- /dev/null +++ b/builder/Generator/Stubs/Sql/update.stub @@ -0,0 +1,2 @@ + +INSERT INTO `{TABLE_NAME}`(`parent_id`, `level`, `name`, `code`, `icon`, `route`, `component`, `redirect`, `is_hidden`, `type`, `status`, `sort`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`, `remark`) VALUES (@id, @level, CONCAT('{NAME}', '更新'), CONCAT('{CODE}',':update'), NULL, NULL, NULL, NULL, '2', 'B', '1', 0, {ADMIN_ID}, NULL, now(), now(), NULL, NULL); diff --git a/builder/Generator/Stubs/Tree/mapper.stub b/builder/Generator/Stubs/Tree/mapper.stub new file mode 100644 index 0000000..991b17b --- /dev/null +++ b/builder/Generator/Stubs/Tree/mapper.stub @@ -0,0 +1,76 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace {NAMESPACE}; + +{USE} +use Hyperf\Database\Model\Builder; +use Builder\Abstracts\AbstractMapper; + +/** + * {COMMENT} + */ +class {CLASS_NAME} extends AbstractMapper +{ + /** + * @var {MODEL} + */ + public $model; + + public function assignModel() + { + $this->model = {MODEL}::class; + } + + /** + * 获取前端选择树 + * @return array + */ + public function getSelectTree(): array + { + return $this->model::query() + ->select(['{FIELD_ID}', '{FIELD_PID}', '{FIELD_ID} AS value', '{FIELD_NAME} AS label']) + ->get()->toTree(); + } + + + /** + * 查询树名称 + * @param array|null $ids + * @return array + */ + public function getTreeName(array $ids = null): array + { + return $this->model::withTrashed()->whereIn('{FIELD_ID}', $ids)->pluck('{FIELD_NAME}')->toArray(); + } + + /** + * @param int $id + * @return bool + */ + public function checkChildrenExists(int $id): bool + { + return $this->model::withTrashed()->where('{FIELD_PID}', $id)->exists(); + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + {SEARCH} + return $query; + } +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Tree/service.stub b/builder/Generator/Stubs/Tree/service.stub new file mode 100644 index 0000000..c9b1429 --- /dev/null +++ b/builder/Generator/Stubs/Tree/service.stub @@ -0,0 +1,134 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace {NAMESPACE}; + + +{USE} +use Builder\Abstracts\AbstractService; + +/** + * {COMMENT} + */ +class {CLASS_NAME} extends AbstractService +{ + /** + * @var {MAPPER} + */ + public $mapper; + + public function __construct({MAPPER} $mapper) + { + $this->mapper = $mapper; + } + + /** + * 获取树列表 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeList(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = false; + return $this->mapper->getTreeList($params, true, '{FIELD_ID}', '{FIELD_PID}'); + } + + /** + * 从回收站获取树列表 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeListByRecycle(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = true; + return $this->mapper->getTreeList($params, true, '{FIELD_ID}', '{FIELD_PID}'); + } + + /** + * 获取前端选择树 + * @return array + */ + public function getSelectTree(): array + { + return $this->mapper->getSelectTree(); + } + + /** + * 新增数据 + * @param array $data + * @return int + */ + public function save(array $data): int + { + return $this->mapper->save($this->handleData($data)); + } + + /** + * 更新 + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + return $this->mapper->update($id, $this->handleData($data)); + } + + /** + * 处理数据 + * @param $data + * @return array + */ + protected function handleData($data): array + { + if (is_array($data['{FIELD_PID}']) && !empty($data['{FIELD_PID}'])) { + $data['{FIELD_PID}'] = array_pop($data['{FIELD_PID}']); + } + return $data; + } + + /** + * 真实删除数据,跳过存在子节点的数据 + * @return array + */ + public function realDel(array $ids): ?array + { + // 存在子节点,跳过的数据 + $ctuIds = []; + if (count($ids)) foreach ($ids as $id) { + if (!$this->checkChildrenExists( (int) $id)) { + $this->mapper->realDelete([$id]); + } else { + array_push($ctuIds, $id); + } + } + return count($ctuIds) ? $this->mapper->getTreeName($ctuIds) : null; + } + + /** + * 检查子节点是否存在 + * @param int $id + * @return bool + */ + public function checkChildrenExists(int $id): bool + { + return $this->mapper->checkChildrenExists($id); + } +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Vue/index.stub b/builder/Generator/Stubs/Vue/index.stub new file mode 100644 index 0000000..5d3d910 --- /dev/null +++ b/builder/Generator/Stubs/Vue/index.stub @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/builder/Generator/Stubs/Vue/numberOperation.stub b/builder/Generator/Stubs/Vue/numberOperation.stub new file mode 100644 index 0000000..94db7ad --- /dev/null +++ b/builder/Generator/Stubs/Vue/numberOperation.stub @@ -0,0 +1,6 @@ + +const numberOperation = (newValue, id, numberName) => { + {BUSINESS_EN_NAME}.numberOperation({ id, numberName, numberValue: newValue }).then( res => { + res.success && Message.success(res.message) + }).catch( e => { console.log(e) } ) +} \ No newline at end of file diff --git a/builder/Generator/Stubs/Vue/switchStatus.stub b/builder/Generator/Stubs/Vue/switchStatus.stub new file mode 100644 index 0000000..e1adab1 --- /dev/null +++ b/builder/Generator/Stubs/Vue/switchStatus.stub @@ -0,0 +1,6 @@ + +const switchStatus = (statusValue, id, statusName) => { + {BUSINESS_EN_NAME}.changeStatus({ id, statusName, statusValue }).then( res => { + res.success && Message.success(res.message) + }).catch( e => { console.log(e) } ) +} diff --git a/builder/Generator/Stubs/config.stub b/builder/Generator/Stubs/config.stub new file mode 100644 index 0000000..9f9083a --- /dev/null +++ b/builder/Generator/Stubs/config.stub @@ -0,0 +1,9 @@ +{ + "name": "{NAME}", + "label": "{LABEL}", + "description": "{DESCRIPTION}", + "installed": true, + "enabled": true, + "version": "{VERSION}", + "order": 0 +} \ No newline at end of file diff --git a/builder/Generator/Stubs/dto.stub b/builder/Generator/Stubs/dto.stub new file mode 100644 index 0000000..de35703 --- /dev/null +++ b/builder/Generator/Stubs/dto.stub @@ -0,0 +1,15 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use Hyperf\Database\Schema\Schema; +use Hyperf\Database\Schema\Blueprint; +use Hyperf\Database\Migrations\Migration; + +class Create{CLASS_NAME}Table extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('{TABLE_NAME}', function (Blueprint $table) { + $table->engine = '{ENGINE}'; + $table->comment('{COMMENT}'); + {COLUMNS} + $table->primary('{ID}'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('DummyTable'); + } +} diff --git a/builder/Generator/Traits/MapperGeneratorTraits.php b/builder/Generator/Traits/MapperGeneratorTraits.php new file mode 100644 index 0000000..97f7963 --- /dev/null +++ b/builder/Generator/Traits/MapperGeneratorTraits.php @@ -0,0 +1,80 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Generator\Traits; + +trait MapperGeneratorTraits +{ + /** + * 获取搜索代码 + * @param $column + * @return string + */ + protected function getSearchCode($column): string + { + return match ($column['query_type']) { + 'neq' => $this->getSearchPHPString($column['column_name'], '!=', $column['column_comment']), + 'gt' => $this->getSearchPHPString($column['column_name'], '<', $column['column_comment']), + 'gte' => $this->getSearchPHPString($column['column_name'], '<=', $column['column_comment']), + 'lt' => $this->getSearchPHPString($column['column_name'], '>', $column['column_comment']), + 'lte' => $this->getSearchPHPString($column['column_name'], '>=', $column['column_comment']), + 'like' => $this->getSearchPHPString($column['column_name'], 'like', $column['column_comment']), + 'between' => $this->getSearchPHPString($column['column_name'], 'between', $column['column_comment']), + default => $this->getSearchPHPString($column['column_name'], '=', $column['column_comment']), + }; + } + + /** + * @param $name + * @param $mark + * @param $comment + * @return string + */ + protected function getSearchPHPString($name, $mark, $comment): string + { + if ($mark == 'like') { + return <<where('{$name}', 'like', '%'.\$params['{$name}'].'%'); + } + +php; + + } + + if ($mark == 'between') { + return <<whereBetween( + '${name}', + [ \$params['${name}'][0], \$params['${name}'][1] ] + ); + } + +php; + } + + return <<where('{$name}', '{$mark}', \$params['{$name}']); + } + +php; + } // 该方法结束位置 +} \ No newline at end of file diff --git a/builder/Generator/VueIndexGenerator.php b/builder/Generator/VueIndexGenerator.php new file mode 100644 index 0000000..3985bf6 --- /dev/null +++ b/builder/Generator/VueIndexGenerator.php @@ -0,0 +1,483 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Generator; + +use App\Setting\Model\SettingGenerateColumns; +use App\Setting\Model\SettingGenerateTables; +use Hyperf\Utils\Filesystem\Filesystem; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; +use Hyperf\Database\Model\Collection; + +/** + * Vue index文件生成 + * Class VueIndexGenerator + * @package Builder\Generator + */ +class VueIndexGenerator extends MineGenerator implements CodeGenerator +{ + /** + * @var SettingGenerateTables + */ + protected SettingGenerateTables $model; + + /** + * @var string + */ + protected string $codeContent; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * @var Collection + */ + protected Collection $columns; + + /** + * 设置生成信息 + * @param SettingGenerateTables $model + * @return VueIndexGenerator + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setGenInfo(SettingGenerateTables $model): VueIndexGenerator + { + $this->model = $model; + $this->filesystem = make(Filesystem::class); + if (empty($model->module_name) || empty($model->menu_name)) { + throw new NormalStatusException(t('setting.gen_code_edit')); + } + $this->columns = SettingGenerateColumns::query() + ->where('table_id', $model->id)->orderByDesc('sort') + ->get([ + 'column_name', 'column_comment', 'allow_roles', 'options', 'is_required', 'is_insert', + 'is_edit', 'is_query', 'is_sort', 'is_pk', 'is_list', 'view_type', 'dict_type', + ]); + + return $this->placeholderReplace(); + } + + /** + * 生成代码 + */ + public function generator(): void + { + $module = Str::lower($this->model->module_name); + $path = BASE_PATH . "/runtime/generate/vue/src/views/{$module}/{$this->getShortBusinessName()}/index.vue"; + $this->filesystem->makeDirectory( + BASE_PATH . "/runtime/generate/vue/src/views/{$module}/{$this->getShortBusinessName()}", + 0755, true, true + ); + $this->filesystem->put($path, $this->replace()->getCodeContent()); + } + + /** + * 预览代码 + */ + public function preview(): string + { + return $this->replace()->getCodeContent(); + } + + /** + * 获取模板地址 + * @return string + */ + protected function getTemplatePath(): string + { + return $this->getStubDir().'/Vue/index.stub'; + } + + /** + * 读取模板内容 + * @return string + */ + protected function readTemplate(): string + { + return $this->filesystem->sharedGet($this->getTemplatePath()); + } + + /** + * 占位符替换 + */ + protected function placeholderReplace(): VueIndexGenerator + { + $this->setCodeContent(str_replace( + $this->getPlaceHolderContent(), + $this->getReplaceContent(), + $this->readTemplate() + )); + + return $this; + } + + /** + * 获取要替换的占位符 + */ + protected function getPlaceHolderContent(): array + { + return [ + '{CODE}', + '{CRUD}', + '{COLUMNS}', + '{BUSINESS_EN_NAME}', + '{INPUT_NUMBER}', + '{SWITCH_STATUS}', + '{MODULE_NAME}', + '{PK}', + ]; + } + + /** + * 获取要替换占位符的内容 + * @return string[] + */ + protected function getReplaceContent(): array + { + return [ + $this->getCode(), + $this->getCrud(), + $this->getColumns(), + $this->getBusinessEnName(), + $this->getInputNumber(), + $this->getSwitchStatus(), + $this->getModuleName(), + $this->getPk(), + ]; + } + + /** + * 获取标识代码 + * @return string + */ + protected function getCode(): string + { + return Str::lower($this->model->module_name) . ':' . $this->getShortBusinessName(); + } + + /** + * 获取CRUD配置代码 + * @return string + */ + protected function getCrud(): string + { + // 配置项 + $options = []; + $options['rowSelection'] = [ 'showCheckedAll' => true ]; + $options['searchLabelWidth'] = "'75px'"; + $options['pk'] = "'".$this->getPk()."'"; + $options['operationColumn'] = false; + $options['operationWidth'] = 160; + $options['viewLayoutSetting'] = [ + 'layout' => "'auto'", + 'cols' => 1, + 'viewType' => $this->model->component_type == 1 ? "'modal'" : "'drawer'", + 'width' => 600, + ]; + $options['api'] = $this->getBusinessEnName() . '.getList'; + if (Str::contains($this->model->generate_menus, 'recycle')) { + $options['recycleApi'] = $this->getBusinessEnName() . '.getRecycleList'; + } + if (Str::contains($this->model->generate_menus, 'save')) { + $options['add'] = [ + 'show' => true, 'api' => $this->getBusinessEnName() . '.save', + 'auth' => "['".$this->getCode().":save']" + ]; + } + if (Str::contains($this->model->generate_menus, 'update')) { + $options['operationColumn'] = true; + $options['edit'] = [ + 'show' => true, 'api' => $this->getBusinessEnName() . '.update', + 'auth' => "['".$this->getCode().":update']" + ]; + } + if (Str::contains($this->model->generate_menus, 'delete')) { + $options['operationColumn'] = true; + $options['delete'] = [ + 'show' => true, + 'api' => $this->getBusinessEnName() . '.deletes', + 'auth' => "['".$this->getCode().":delete']" + ]; + if (Str::contains($this->model->generate_menus, 'recycle')) { + $options['delete']['realApi'] = $this->getBusinessEnName() . '.realDeletes'; + $options['delete']['realAuth'] = "['".$this->getCode().":realDeletes']"; + $options['recovery'] = [ + 'show' => true, + 'api' => $this->getBusinessEnName() . '.recoverys', + 'auth' => "['".$this->getCode().":recovery']" + ]; + } + } + $requestRoute = Str::lower($this->model->module_name) . '/' . $this->getShortBusinessName(); + // 导入 + if (Str::contains($this->model->generate_menus, 'import')) { + $options['import'] = [ + 'show' => true, + 'url' => "'".$requestRoute . '/import'."'", + 'templateUrl' => "'".$requestRoute . '/downloadTemplate'."'", + 'auth' => "['".$this->getCode().":import']" + ]; + } + // 导出 + if (Str::contains($this->model->generate_menus, 'export')) { + $options['export'] = [ + 'show' => true, + 'url' => "'".$requestRoute . '/export'."'", + 'auth' => "['".$this->getCode().":export']" + ]; + } + return 'const crud = reactive(' . $this->jsonFormat($options, true) . ')'; + } + + /** + * 获取列配置代码 + * @return string + */ + protected function getColumns(): string + { + // 字段配置项 + $options = []; + foreach ($this->columns as $column) { + $tmp = [ + 'title' => $column->column_comment, + 'dataIndex' => $column->column_name, + 'formType' => $this->getViewType($column->view_type), + ]; + // 基础 + if ($column->is_query == self::YES) { + $tmp['search'] = true; + } + if ($column->is_insert == self::NO) { + $tmp['addDisplay'] = false; + } + if ($column->is_edit == self::NO) { + $tmp['editDisplay'] = false; + } + if ($column->is_list == self::NO) { + $tmp['hide'] = true; + } + if ($column->is_required == self::YES) { + $tmp['rules'] = [ + 'required' => true, + 'message' => '请输入' . $column->column_comment + ]; + } + if ($column->is_sort == self::YES) { + $tmp['sortable'] = [ + 'sortDirections' => [ 'ascend', 'descend' ], + 'sorter' => true + ]; + } + // 扩展项 + if (!empty($column->options)) { + $collection = $column->options['collection']; + // 合并 + $tmp = array_merge($tmp, $column->options); + // 自定义数据 + if (in_array($column->view_type, ['checkbox', 'radio', 'select', 'transfer']) && !empty($collection)) { + $tmp['dict'] = [ 'data' => $collection, 'translation' => true ]; + } + // 对日期时间处理 + if ($column->view_type == 'date' && $column->options['mode'] == 'date') { + unset($tmp['mode']); + if (isset($column->options['range']) && $column->options['range']) { + $tmp['formType'] = 'range'; + unset($tmp['range']); + } + } + unset($tmp['collection']); + } + // 字典 + if (!empty($column->dict_type)) { + $tmp['dict'] = [ + 'name' => $column->dict_type, + 'props' => [ 'label' => 'title', 'value' => 'key' ], + 'translation' => true + ]; + } + // 密码处理 + if ($column->view_type == 'password') { + $tmp['type'] = 'password'; + } + // 允许查看字段的角色(前端还待支持) + // todo... + $options[] = $tmp; + } + return 'const columns = reactive(' . $this->jsonFormat($options) . ')'; + } + + /** + * @return string + */ + protected function getShowRecycle(): string + { + return ( strpos($this->model->generate_menus, 'recycle') > 0 ) ? 'true' : 'false'; + } + + /** + * 获取业务英文名 + * @return string + */ + protected function getBusinessEnName(): string + { + return Str::camel(str_replace(env('DB_PREFIX'), '', $this->model->table_name)); + } + + /** + * @return string + */ + protected function getModuleName(): string + { + return Str::lower($this->model->module_name); + } + + /** + * 返回主键 + * @return string + */ + protected function getPk(): string + { + foreach ($this->columns as $column) { + if ($column->is_pk == self::YES) { + return $column->column_name; + } + } + return ''; + } + + /** + * 计数器组件方法 + * @return string + * @noinspection BadExpressionStatementJS + */ + protected function getInputNumber(): string + { + if (in_array('numberOperation' , explode(',', $this->model->generate_menus))) { + return str_replace('{BUSINESS_EN_NAME}', $this->getBusinessEnName(), $this->getOtherTemplate('numberOperation')); + } + return ''; + } + + /** + * 计数器组件方法 + * @return string + * @noinspection BadExpressionStatementJS + */ + protected function getSwitchStatus(): string + { + if (in_array('changeStatus' , explode(',', $this->model->generate_menus))) { + return str_replace('{BUSINESS_EN_NAME}', $this->getBusinessEnName(), $this->getOtherTemplate('switchStatus')); + } + return ''; + } + + /** + * @param string $tpl + * @return string + */ + protected function getOtherTemplate(string $tpl): string + { + return $this->filesystem->sharedGet($this->getStubDir() . "/Vue/{$tpl}.stub"); + } + + /** + * 获取短业务名称 + * @return string + */ + public function getShortBusinessName(): string + { + return Str::camel(str_replace( + Str::lower($this->model->module_name), + '', + str_replace(env('DB_PREFIX'), '', $this->model->table_name) + )); + } + + /** + * 视图组件 + * @param string $viewType + * @return string + */ + protected function getViewType(string $viewType): string + { + $viewTypes = [ + 'text' => 'input', + 'password' => 'input', + 'textarea' => 'textarea', + 'inputNumber' => 'input-number', + 'inputTag' => 'input-tag', + 'mention' => 'mention', + 'switch' => 'switch', + 'slider' => 'slider', + 'select' => 'select', + 'radio' => 'radio', + 'checkbox' => 'checkbox', + 'treeSelect' => 'tree-select', + 'date' => 'date', + 'time' => 'time', + 'rate' => 'rate', + 'cascader' => 'cascader', + 'transfer' => 'transfer', + 'selectUser' => 'select-user', + 'userInfo' => 'user-info', + 'cityLinkage' => 'city-linkage', + 'icon' => 'icon', + 'formGroup' => 'form-group', + 'upload' => 'upload', + 'selectResource' => 'select-resource', + 'editor' => 'editor', + 'codeEditor' => 'code-editor', + ]; + + return $viewTypes[$viewType] ?? 'input'; + } + + /** + * array 到 json 数据格式化 + * @param array $data + * @param bool $removeValueQuotes + * @return string + */ + protected function jsonFormat(array $data, bool $removeValueQuotes = false): string + { + $data = str_replace(' ', ' ', json_encode($data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT)); + $data = str_replace(['"true"', '"false"', "\\"], [ true, false, ''], $data); + $data = preg_replace('/(\s+)\"(.+)\":/', "\\1\\2:", $data); + if ($removeValueQuotes) { + $data = preg_replace('/(:\s)\"(.+)\"/', "\\1\\2", $data); + } + return $data; + } + + /** + * 设置代码内容 + * @param string $content + */ + public function setCodeContent(string $content) + { + $this->codeContent = $content; + } + + /** + * 获取代码内容 + * @return string + */ + public function getCodeContent(): string + { + return $this->codeContent; + } + +} \ No newline at end of file diff --git a/builder/Helper/AppVerify.php b/builder/Helper/AppVerify.php new file mode 100644 index 0000000..0bf4978 --- /dev/null +++ b/builder/Helper/AppVerify.php @@ -0,0 +1,112 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Helper; + +use Xmo\JWTAuth\JWT; +use Psr\SimpleCache\InvalidArgumentException; + +class AppVerify +{ + /** + * @var JWT + */ + protected JWT $jwt; + + + /** + * AppVerify constructor. + * @param string $scene 场景,默认为default + */ + public function __construct(string $scene = 'api') + { + /* @var JWT $this->jwt */ + $this->jwt = make(JWT::class)->setScene($scene); + } + + /** + * 验证token + * @param String|null $token + * @param string $scene + * @return bool + * @throws InvalidArgumentException + */ + public function check(?String $token = null, string $scene = 'api'): bool + { + try { + if ($this->jwt->checkToken($token, $scene, true, true, true)) { + return true; + } + } catch (\Throwable $e) { + return false; + } + + return false; + } + + /** + * 获取JWT对象 + * @return Jwt + */ + public function getJwt(): Jwt + { + return $this->jwt; + } + + /** + * 获取当前API的信息 + * @return array + */ + public function getUserInfo(): array + { + return $this->jwt->getParserData(); + } + + /** + * 获取当前ID + * @return string + */ + public function getId(): string + { + return (string) $this->jwt->getParserData()['id']; + } + + /** + * 获取当前APP_ID + * @return string + */ + public function getAppId(): string + { + return (string) $this->jwt->getParserData()['app_id']; + } + + /** + * 获取Token + * @param array $apiInfo + * @return string + * @throws InvalidArgumentException + */ + public function getToken(array $apiInfo): string + { + return $this->jwt->getToken($apiInfo); + } + + /** + * 刷新token + * @return string + * @throws InvalidArgumentException + */ + public function refresh(): string + { + return $this->jwt->refreshToken(); + } +} \ No newline at end of file diff --git a/builder/Helper/ConsoleTable.php b/builder/Helper/ConsoleTable.php new file mode 100644 index 0000000..62d294a --- /dev/null +++ b/builder/Helper/ConsoleTable.php @@ -0,0 +1,300 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace Builder\Helper; + +class ConsoleTable +{ + const ALIGN_LEFT = 1; + const ALIGN_RIGHT = 0; + const ALIGN_CENTER = 2; + + /** + * 头信息数据 + * @var array + */ + protected array $header = []; + + /** + * 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @var int + */ + protected int $headerAlign = 1; + + /** + * 表格数据(二维数组) + * @var array + */ + protected array $rows = []; + + /** + * 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @var int + */ + protected int $cellAlign = 1; + + /** + * 单元格宽度信息 + * @var array + */ + protected array $colWidth = []; + + /** + * 表格输出样式 + * @var string + */ + protected string $style = 'default'; + + /** + * 表格样式定义 + * @var array + */ + protected array $format = [ + 'compact' => [], + 'default' => [ + 'top' => ['+', '-', '+', '+'], + 'cell' => ['|', ' ', '|', '|'], + 'middle' => ['+', '-', '+', '+'], + 'bottom' => ['+', '-', '+', '+'], + 'cross-top' => ['+', '-', '-', '+'], + 'cross-bottom' => ['+', '-', '-', '+'], + ], + 'markdown' => [ + 'top' => [' ', ' ', ' ', ' '], + 'cell' => ['|', ' ', '|', '|'], + 'middle' => ['|', '-', '|', '|'], + 'bottom' => [' ', ' ', ' ', ' '], + 'cross-top' => ['|', ' ', ' ', '|'], + 'cross-bottom' => ['|', ' ', ' ', '|'], + ], + 'borderless' => [ + 'top' => ['=', '=', ' ', '='], + 'cell' => [' ', ' ', ' ', ' '], + 'middle' => ['=', '=', ' ', '='], + 'bottom' => ['=', '=', ' ', '='], + 'cross-top' => ['=', '=', ' ', '='], + 'cross-bottom' => ['=', '=', ' ', '='], + ], + 'box' => [ + 'top' => ['┌', '─', '┬', '┐'], + 'cell' => ['│', ' ', '│', '│'], + 'middle' => ['├', '─', '┼', '┤'], + 'bottom' => ['└', '─', '┴', '┘'], + 'cross-top' => ['├', '─', '┴', '┤'], + 'cross-bottom' => ['├', '─', '┬', '┤'], + ], + 'box-double' => [ + 'top' => ['╔', '═', '╤', '╗'], + 'cell' => ['║', ' ', '│', '║'], + 'middle' => ['╠', '─', '╪', '╣'], + 'bottom' => ['╚', '═', '╧', '╝'], + 'cross-top' => ['╠', '═', '╧', '╣'], + 'cross-bottom' => ['╠', '═', '╤', '╣'], + ], + ]; + + /** + * 设置表格头信息 以及对齐方式 + * @access public + * @param array $header 要输出的Header信息 + * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @return void + */ + public function setHeader(array $header, int $align = 1): void + { + $this->header = $header; + $this->headerAlign = $align; + + $this->checkColWidth($header); + } + + /** + * 设置输出表格数据 及对齐方式 + * @access public + * @param array $rows 要输出的表格数据(二维数组) + * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @return void + */ + public function setRows(array $rows, int $align = 1): void + { + $this->rows = $rows; + $this->cellAlign = $align; + + foreach ($rows as $row) { + $this->checkColWidth($row); + } + } + + /** + * 设置全局单元格对齐方式 + * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @return $this + */ + public function setCellAlign(int $align = 1) + { + $this->cellAlign = $align; + return $this; + } + + /** + * 检查列数据的显示宽度 + * @access public + * @param mixed $row 行数据 + * @return void + */ + protected function checkColWidth($row): void + { + if (is_array($row)) { + foreach ($row as $key => $cell) { + $width = mb_strwidth((string) $cell); + if (!isset($this->colWidth[$key]) || $width > $this->colWidth[$key]) { + $this->colWidth[$key] = $width; + } + } + } + } + + /** + * 增加一行表格数据 + * @access public + * @param mixed $row 行数据 + * @param bool $first 是否在开头插入 + * @return void + */ + public function addRow($row, bool $first = false): void + { + if ($first) { + array_unshift($this->rows, $row); + } else { + $this->rows[] = $row; + } + + $this->checkColWidth($row); + } + + /** + * 设置输出表格的样式 + * @access public + * @param string $style 样式名 + * @return void + */ + public function setStyle(string $style): void + { + $this->style = isset($this->format[$style]) ? $style : 'default'; + } + + /** + * 输出分隔行 + * @access public + * @param string $pos 位置 + * @return string + */ + protected function renderSeparator(string $pos): string + { + $style = $this->getStyle($pos); + $array = []; + + foreach ($this->colWidth as $width) { + $array[] = str_repeat($style[1], $width + 2); + } + + return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL; + } + + /** + * 输出表格头部 + * @access public + * @return string + */ + protected function renderHeader(): string + { + $style = $this->getStyle('cell'); + $content = $this->renderSeparator('top'); + + foreach ($this->header as $key => $header) { + $array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign); + } + + if (!empty($array)) { + $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL; + + if (!empty($this->rows)) { + $content .= $this->renderSeparator('middle'); + } + } + + return $content; + } + + protected function getStyle(string $style): array + { + if ($this->format[$this->style]) { + $style = $this->format[$this->style][$style]; + } else { + $style = [' ', ' ', ' ', ' ']; + } + + return $style; + } + + /** + * 输出表格 + * @access public + * @param array $dataList 表格数据 + * @return string + */ + public function render(array $dataList = []): string + { + if (!empty($dataList)) { + $this->setRows($dataList); + } + + // 输出头部 + $content = $this->renderHeader(); + $style = $this->getStyle('cell'); + + if (!empty($this->rows)) { + foreach ($this->rows as $row) { + if (is_string($row) && '-' === $row) { + $content .= $this->renderSeparator('middle'); + } elseif (is_scalar($row)) { + $content .= $this->renderSeparator('cross-top'); + $width = 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) { + return $a + $b; + }); + $array = str_pad($row, $width); + + $content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL; + $content .= $this->renderSeparator('cross-bottom'); + } else { + $array = []; + + foreach ($row as $key => $val) { + $width = $this->colWidth[$key]; + // form https://github.com/symfony/console/blob/20c9821c8d1c2189f287dcee709b2f86353ea08f/Helper/Table.php#L467 + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding((string) $val, null, true)) { + $width += strlen((string) $val) - mb_strwidth((string) $val, $encoding); + } + $array[] = ' ' . str_pad((string) $val, $width, ' ', $this->cellAlign); + } + + $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL; + } + } + } + + $content .= $this->renderSeparator('bottom'); + + return $content; + } +} diff --git a/builder/Helper/Id.php b/builder/Helper/Id.php new file mode 100644 index 0000000..8c12b56 --- /dev/null +++ b/builder/Helper/Id.php @@ -0,0 +1,106 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Helper; + +class Id +{ + const TWEPOCH = 1620750646000; // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) + + const WORKER_ID_BITS = 2; // 机器标识位数 + const DATACENTER_ID_BITS = 2; // 数据中心标识位数 + const SEQUENCE_BITS = 5; // 毫秒内自增位 + + private $workerId; // 工作机器ID + private $datacenterId; // 数据中心ID + private $sequence; // 毫秒内序列 + + private $maxWorkerId = -1 ^ (-1 << self::WORKER_ID_BITS); // 机器ID最大值 + private $maxDatacenterId = -1 ^ (-1 << self::DATACENTER_ID_BITS); // 数据中心ID最大值 + + private $workerIdShift = self::SEQUENCE_BITS; // 机器ID偏左移位数 + private $datacenterIdShift = self::SEQUENCE_BITS + self::WORKER_ID_BITS; // 数据中心ID左移位数 + private $timestampLeftShift = self::SEQUENCE_BITS + self::WORKER_ID_BITS + self::DATACENTER_ID_BITS; // 时间毫秒左移位数 + private $sequenceMask = -1 ^ (-1 << self::SEQUENCE_BITS); // 生成序列的掩码 + + private $lastTimestamp = -1; // 上次生产id时间戳 + + public function __construct($workerId = 1, $datacenterId = 1, $sequence = 0) + { + if ($workerId > $this->maxWorkerId || $workerId < 0) { + throw new \Exception("worker Id can't be greater than {$this->maxWorkerId} or less than 0"); + } + + if ($datacenterId > $this->maxDatacenterId || $datacenterId < 0) { + throw new \Exception("datacenter Id can't be greater than {$this->maxDatacenterId} or less than 0"); + } + + $this->workerId = $workerId; + $this->datacenterId = $datacenterId; + $this->sequence = $sequence; + } + + /** + * @return int + * @throws \Exception + */ + public function getId(?int $workerId = null) + { + $timestamp = $this->timeGen(); + + if (! is_null($workerId)) { + $this->workerId = $workerId; + } + + if ($timestamp < $this->lastTimestamp) { + $diffTimestamp = $this->lastTimestamp - $timestamp; + throw new \Exception("Clock moved backwards. Refusing to generate id for {$diffTimestamp} milliseconds"); + } + + if ($this->lastTimestamp == $timestamp) { + $this->sequence = ($this->sequence + 1) & $this->sequenceMask; + + if (0 == $this->sequence) { + $timestamp = $this->tilNextMillis($this->lastTimestamp); + } + } else { + $this->sequence = 0; + } + + $this->lastTimestamp = $timestamp; + + return (($timestamp - self::TWEPOCH) << $this->timestampLeftShift) | + ($this->datacenterId << $this->datacenterIdShift) | + ($this->workerId << $this->workerIdShift) | + $this->sequence; + } + + protected function tilNextMillis($lastTimestamp) + { + $timestamp = $this->timeGen(); + while ($timestamp <= $lastTimestamp) { + $timestamp = $this->timeGen(); + } + + return $timestamp; + } + + protected function timeGen() + { + return floor(microtime(true) * 1000); + } + + // 左移 << + protected function leftShift($a, $b) + { + return bcmul($a, bcpow(2, $b)); + } +} \ No newline at end of file diff --git a/builder/Helper/LoginUser.php b/builder/Helper/LoginUser.php new file mode 100644 index 0000000..e8eb520 --- /dev/null +++ b/builder/Helper/LoginUser.php @@ -0,0 +1,188 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Helper; + +use App\System\Model\SystemRole; +use App\System\Model\SystemUser; +use App\System\Service\SystemUserService; +use Psr\SimpleCache\InvalidArgumentException; +use Xmo\JWTAuth\JWT; + +use Builder\Exception\TokenException; +use Builder\MineRequest; + +class LoginUser +{ + /** + * @var JWT + */ + protected JWT $jwt; + + /** + * @var MineRequest + */ + protected MineRequest $request; + + + /** + * LoginUser constructor. + * @param string $scene 场景,默认为default + */ + public function __construct(string $scene = 'default') + { + /* @var JWT $this->jwt */ + $this->jwt = make(JWT::class)->setScene($scene); + } + + /** + * 验证token + * @param string|null $token + * @param string $scene + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function check(?string $token = null, string $scene = 'default'): bool + { + try { + if ($this->jwt->checkToken($token, $scene, true, true, true)) { + return true; + } + } catch (InvalidArgumentException $e) { + throw new TokenException(t('jwt.no_token')); + } catch (\Throwable $e) { + throw new TokenException(t('jwt.no_login')); + } + + return false; + } + + /** + * 获取JWT对象 + * @return Jwt + */ + public function getJwt(): Jwt + { + return $this->jwt; + } + + /** + * 获取当前登录用户信息 + * @param string|null $token + * @return array + */ + public function getUserInfo(?string $token = null): array + { + return $this->jwt->getParserData($token); + } + + /** + * 获取当前登录用户ID + * @return int + */ + public function getId(): int + { + return $this->jwt->getParserData()['id']; + } + + /** + * 获取当前登录用户名 + * @return string + */ + public function getUsername(): string + { + return $this->jwt->getParserData()['username']; + } + + /** + * 获取当前登录用户角色 + * @param array $columns + * @return array + */ + public function getUserRole(array $columns = ['id', 'name', 'code']): array + { + return SystemUser::find($this->getId(), ['id'])->roles()->get($columns)->toArray(); + } + + /** + * 获取当前登录用户岗位 + * @param array $columns + * @return array + */ + public function getUserPost(array $columns = ['id', 'name', 'code']): array + { + return SystemUser::find($this->getId(), ['id'])->posts()->get($columns)->toArray(); + } + + /** + * 获取当前token用户类型 + * @return string + */ + public function getUserType(): string + { + return $this->jwt->getParserData()['user_type']; + } + + /** + * 获取当前token用户部门ID + * @return int + */ + public function getDeptId(): int + { + return (int) $this->jwt->getParserData()['dept_id']; + } + + /** + * 是否为超级管理员(创始人),用户禁用对创始人没用 + * @return bool + */ + public function isSuperAdmin(): bool + { + return env('SUPER_ADMIN') == $this->getId(); + } + + /** + * 是否为管理员角色 + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function isAdminRole(): bool + { + return in_array( + SystemRole::find(env('ADMIN_ROLE'), ['code'])->code, + container()->get(SystemUserService::class)->getInfo()['roles'] + ); + } + + /** + * 获取Token + * @param array $user + * @return string + * @throws InvalidArgumentException + */ + public function getToken(array $user): string + { + return $this->jwt->getToken($user); + } + + /** + * 刷新token + * @return string + * @throws InvalidArgumentException + */ + public function refresh(): string + { + return $this->jwt->refreshToken(); + } +} \ No newline at end of file diff --git a/builder/Helper/MineCaptcha.php b/builder/Helper/MineCaptcha.php new file mode 100644 index 0000000..0c2ee9a --- /dev/null +++ b/builder/Helper/MineCaptcha.php @@ -0,0 +1,28 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Helper; + +class MineCaptcha +{ + /** + * @return array + */ + public function getCaptchaInfo(): array + { + $conf = new \EasySwoole\VerifyCode\Conf(); + $conf->setUseCurve()->setUseNoise(); + $validCode = new \EasySwoole\VerifyCode\VerifyCode($conf); + $draw = $validCode->DrawCode(); + return ['code' => \Builder\Helper\Str::lower($draw->getImageCode()), 'image' => $draw->getImageByte()]; + } +} \ No newline at end of file diff --git a/builder/Helper/MineCode.php b/builder/Helper/MineCode.php new file mode 100644 index 0000000..0d3bfa9 --- /dev/null +++ b/builder/Helper/MineCode.php @@ -0,0 +1,45 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Helper; + + +class MineCode +{ + public const TOKEN_EXPIRED = 1001; // TOKEN过期、不存在 + public const VALIDATE_FAILED = 1002; // 数据验证失败 + public const NO_PERMISSION = 1003; // 没有权限 + public const NO_DATA = 1004; // 没有数据 + public const NORMAL_STATUS = 1005; // 正常状态异常代码 + + public const NO_USER = 1010; // 用户不存在 + public const PASSWORD_ERROR = 1011; // 密码错误 + public const USER_BAN = 1012; // 用户被禁 + + public const METHOD_NOT_ALLOW = 2000; // 地址使用了不允许的访问方法 + public const NOT_FOUND = 2100; // 资源不存在 + public const INTERFACE_EXCEPTION = 2150;// 接口异常 + public const RESOURCE_STOP = 2200; // 资源被停用 + + public const APP_BAN = 2300; // APP被停用 + + public const API_AUTH_EXCEPTION = 10000; // 接口鉴权异常 + public const API_AUTH_FAIL = 10010; // 接口鉴权失败 + public const API_APP_ID_MISSING = 10101; // 缺少APP ID + public const API_APP_SECRET_MISSING = 10102; // 缺少APP SECRET + public const API_ACCESS_TOKEN_MISSING = 10103; // 缺少复杂模式 ACCESS TOKEN + public const API_PARAMS_ERROR = 10104; // 复杂模式 ACCESS TOKEN 参数错误 + public const API_SIGN_MISSING = 10105; // 缺少签名 + public const API_SIGN_ERROR = 10106; // 签名错误 + public const API_IDENTITY_MISSING = 10107; // 缺少简易模式 identity + public const API_IDENTITY_ERROR = 10108; // 简易模式 identity 错误 + public const API_VERIFY_PASS = 10160; // API验证通过 +} \ No newline at end of file diff --git a/builder/Helper/Str.php b/builder/Helper/Str.php new file mode 100644 index 0000000..68600c0 --- /dev/null +++ b/builder/Helper/Str.php @@ -0,0 +1,329 @@ + +// +---------------------------------------------------------------------- +namespace Builder\Helper; + +class Str +{ + + protected static $snakeCache = []; + + protected static $camelCache = []; + + protected static $studlyCache = []; + + /** + * 检查字符串中是否包含某些字符串 + * @param string $haystack + * @param string|array $needles + * @return bool + */ + public static function contains(string $haystack, $needles): bool + { + foreach ((array) $needles as $needle) { + if ('' != $needle && mb_strpos($haystack, $needle) !== false) { + return true; + } + } + + return false; + } + + /** + * 检查字符串是否以某些字符串结尾 + * + * @param string $haystack + * @param string|array $needles + * @return bool + */ + public static function endsWith(string $haystack, $needles): bool + { + foreach ((array) $needles as $needle) { + if ((string) $needle === static::substr($haystack, -static::length($needle))) { + return true; + } + } + + return false; + } + + /** + * 检查字符串是否以某些字符串开头 + * + * @param string $haystack + * @param string|array $needles + * @return bool + */ + public static function startsWith(string $haystack, $needles): bool + { + foreach ((array) $needles as $needle) { + if ('' != $needle && mb_strpos($haystack, $needle) === 0) { + return true; + } + } + + return false; + } + + /** + * 获取指定长度的随机字母数字组合的字符串 + * + * @param int $length + * @param int $type + * @param string $addChars + * @return string + */ + public static function random(int $length = 6, int $type = -1, string $addChars = ''): string + { + $str = ''; + switch ($type) { + case 0: + $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars; + break; + case 1: + $chars = str_repeat('0123456789', 3); + break; + case 2: + $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars; + break; + case 3: + $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars; + break; + case 4: + $chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书" . $addChars; + break; + default: + $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars; + break; + } + if ($length > 10) { + $chars = $type == 1 ? str_repeat($chars, $length) : str_repeat($chars, 5); + } + if ($type != 4) { + $chars = str_shuffle($chars); + $str = substr($chars, 0, $length); + } else { + for ($i = 0; $i < $length; $i++) { + $str .= mb_substr($chars, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 1); + } + } + return $str; + } + + /** + * 字符串转小写 + * + * @param string $value + * @return string + */ + public static function lower(string $value): string + { + return mb_strtolower($value, 'UTF-8'); + } + + /** + * 字符串转大写 + * + * @param string $value + * @return string + */ + public static function upper(string $value): string + { + return mb_strtoupper($value, 'UTF-8'); + } + + /** + * 获取字符串的长度 + * + * @param string $value + * @return int + */ + public static function length(string $value): int + { + return mb_strlen($value); + } + + /** + * 截取字符串 + * + * @param string $string + * @param int $start + * @param int|null $length + * @return string + */ + public static function substr(string $string, int $start, int $length = null): string + { + return mb_substr($string, $start, $length, 'UTF-8'); + } + + /** + * 驼峰转下划线 + * + * @param string $value + * @param string $delimiter + * @return string + */ + public static function snake(string $value, string $delimiter = '_'): string + { + $key = $value; + + if (isset(static::$snakeCache[$key][$delimiter])) { + return static::$snakeCache[$key][$delimiter]; + } + + if (!ctype_lower($value)) { + $value = preg_replace('/\s+/u', '', $value); + + $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value)); + } + + return static::$snakeCache[$key][$delimiter] = $value; + } + + /** + * 下划线转驼峰(首字母小写) + * + * @param string $value + * @return string + */ + public static function camel(string $value): string + { + if (isset(static::$camelCache[$value])) { + return static::$camelCache[$value]; + } + + return static::$camelCache[$value] = lcfirst(static::studly($value)); + } + + /** + * 下划线转驼峰(首字母大写) + * + * @param string $value + * @return string + */ + public static function studly(string $value): string + { + $key = $value; + + if (isset(static::$studlyCache[$key])) { + return static::$studlyCache[$key]; + } + + $value = ucwords(str_replace(['-', '_'], ' ', $value)); + + return static::$studlyCache[$key] = str_replace(' ', '', $value); + } + + /** + * 转为首字母大写的标题格式 + * + * @param string $value + * @return string + */ + public static function title(string $value): string + { + return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8'); + } + + /** + * 获取IP的区域地址 + * @param string $ip + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public static function ipToRegion(string $ip): string + { + $ip2Region = make(\Ip2Region::class); + if (empty($ip2Region->btreeSearch($ip)['region'])) { + return t('jwt.unknown'); + } + $region = $ip2Region->btreeSearch($ip)['region']; + list($country, $number, $province, $city, $network) = explode('|', $region); + if ($country == '中国') { + return $province . '-' . $city . ':' . $network; + } else if ($country == '0') { + return t('jwt.unknown'); + } else { + return $country; + } + } + + /** + * 秒数转时分格式 + * @param $time int + * @return string + */ + public static function Sec2Time(int $time): string + { + $value = [ + 'years' => 0, 'days' => 0, 'hours' => 0, + 'minutes' => 0, 'seconds' => 0, + ]; + + if ($time >= 31556926) { + $value['years'] = floor($time/31556926); + $time = ($time%31556926); + } + + if ($time >= 86400) { + $value['days'] = floor($time/86400); + $time = ($time%86400); + } + + if ($time >= 3600) { + $value['hours'] = floor($time/3600); + $time = ($time%3600); + } + + if ($time >= 60) { + $value['minutes'] = floor($time/60); + $time = ($time%60); + } + + $value['seconds'] = floor($time); + + return $value['years'] . '年' . $value['days'] . '天 '. $value["hours"] . '小时'. $value['minutes'] . '分'.$value['seconds'].'秒'; + + } + + /** + * 生成UUID + * @return string + */ + public static function getUUID(): string + { + $chars = md5(uniqid((string)mt_rand(), true)); + return substr ( $chars, 0, 8 ) . '-' + . substr ( $chars, 8, 4 ) . '-' + . substr ( $chars, 12, 4 ) . '-' + . substr ( $chars, 16, 4 ) . '-' + . substr ( $chars, 20, 12 ); + } + + /** + * Replace the first occurrence of a given value in the string. + */ + public static function replaceFirst(string $search, string $replace, string $subject, int &$offset = 0): string + { + if ($search == '') { + return $subject; + } + + $position = strpos($subject, $search, $offset); + + if ($position !== false) { + $offset = $position + strlen($replace); + return substr_replace($subject, $replace, $position, strlen($search)); + } + + return $subject; + } +} diff --git a/builder/Helper/functions.php b/builder/Helper/functions.php new file mode 100644 index 0000000..42b1063 --- /dev/null +++ b/builder/Helper/functions.php @@ -0,0 +1,238 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +use App\System\Vo\QueueMessageVo; +use Hyperf\Contract\StdoutLoggerInterface; +use Hyperf\Logger\LoggerFactory; +use Hyperf\Utils\ApplicationContext; +use Builder\Helper\LoginUser; +use Builder\Helper\AppVerify; +use Builder\Helper\Id; +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Log\LoggerInterface; + +if (! function_exists('container')) { + + /** + * 获取容器实例 + * @return \Psr\Container\ContainerInterface + */ + function container(): \Psr\Container\ContainerInterface + { + return ApplicationContext::getContainer(); + } + +} + +if (! function_exists('redis')) { + + /** + * 获取Redis实例 + * @return \Hyperf\Redis\Redis + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function redis(): \Hyperf\Redis\Redis + { + return container()->get(\Hyperf\Redis\Redis::class); + } + +} + +if (! function_exists('console')) { + + /** + * 获取控制台输出实例 + * @return StdoutLoggerInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function console(): StdoutLoggerInterface + { + return container()->get(StdoutLoggerInterface::class); + } + +} + +if (! function_exists('logger')) { + + /** + * 获取日志实例 + * @param string $name + * @return LoggerInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function logger(string $name = 'Log'): LoggerInterface + { + return container()->get(LoggerFactory::class)->get($name); + } + +} + +if (! function_exists('user')) { + /** + * 获取当前登录用户实例 + * @param string $scene + * @return LoginUser + */ + function user(string $scene = 'default'): LoginUser + { + return new LoginUser($scene); + } +} + +if (! function_exists('format_size')) { + /** + * 格式化大小 + * @param int $size + * @return string + */ + function format_size(int $size): string + { + $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + $index = 0; + for ($i = 0; $size >= 1024 && $i < 5; $i++) { + $size /= 1024; + $index = $i; + } + return round($size, 2) . $units[$index]; + } +} + +if (! function_exists('t')) { + /** + * 多语言函数 + * @param string $key + * @param array $replace + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function t(string $key, array $replace = []): string + { + $acceptLanguage = container()->get(\Builder\MineRequest::class)->getHeaderLine('accept-language'); + $language = !empty($acceptLanguage) ? explode(',',$acceptLanguage)[0] : 'zh_CN'; + return __($key, $replace, $language); + } +} + +if (! function_exists('mine_collect')) { + /** + * 创建一个Mine的集合类 + * @param null|mixed $value + * @return \Builder\MineCollection + */ + function mine_collect($value = null): \Builder\MineCollection + { + return new \Builder\MineCollection($value); + } +} + +if (! function_exists('context_set')) { + /** + * 设置上下文数据 + * @param string $key + * @param $data + * @return bool + */ + function context_set(string $key, $data): bool + { + return (bool)\Hyperf\Context\Context::set($key, $data); + } +} + +if (! function_exists('context_get')) { + /** + * 获取上下文数据 + * @param string $key + * @return mixed + */ + function context_get(string $key) + { + return \Hyperf\Context\Context::get($key); + } +} + +if (! function_exists('app_verify')) { + /** + * 获取APP应用请求实例 + * @param string $scene + * @return AppVerify + */ + function app_verify(string $scene = 'api'): AppVerify + { + return new AppVerify($scene); + } +} + +if (! function_exists('snowflake_id')) { + /** + * 生成雪花ID + * @param int|null $workerId + * @return String + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function snowflake_id(?int $workerId = null): String + { + return container()->get(Id::class)->getId($workerId); + } +} + +if (! function_exists('event')) { + /** + * 事件调度快捷方法 + * @param object $dispatch + * @return object + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function event(object $dispatch): object + { + return container()->get(EventDispatcherInterface::class)->dispatch($dispatch); + } +} + +if (! function_exists('push_queue_message')) { + /** + * 推送消息到队列 + * @param QueueMessageVo $message + * @param array $receiveUsers + * @return bool + * @throws Throwable + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function push_queue_message(QueueMessageVo $message, array $receiveUsers = []): bool + { + return container() + ->get(\App\System\Service\SystemQueueLogService::class) + ->pushMessage($message, $receiveUsers); + } +} + +if (! function_exists('add_queue')) { + /** + * 添加任务到队列 + * @param \App\System\Vo\AmqpQueueVo $amqpQueueVo + * @return bool + * @throws Throwable + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + function add_queue(\App\System\Vo\AmqpQueueVo $amqpQueueVo): bool + { + return container() + ->get(\App\System\Service\SystemQueueLogService::class) + ->addQueue($amqpQueueVo); + } +} \ No newline at end of file diff --git a/builder/Interfaces/KeyValueEnum.php b/builder/Interfaces/KeyValueEnum.php new file mode 100644 index 0000000..3df976c --- /dev/null +++ b/builder/Interfaces/KeyValueEnum.php @@ -0,0 +1,25 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Interfaces; + +/** + * key/value 枚举接口 + */ +interface KeyValueEnum +{ + public function key(); + + public function value(); +} \ No newline at end of file diff --git a/builder/Interfaces/MineModelExcel.php b/builder/Interfaces/MineModelExcel.php new file mode 100644 index 0000000..3826e5c --- /dev/null +++ b/builder/Interfaces/MineModelExcel.php @@ -0,0 +1,19 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Interfaces; + +interface MineModelExcel +{ + +} \ No newline at end of file diff --git a/builder/Interfaces/MineRedisInterface.php b/builder/Interfaces/MineRedisInterface.php new file mode 100644 index 0000000..9f0f15c --- /dev/null +++ b/builder/Interfaces/MineRedisInterface.php @@ -0,0 +1,27 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Interfaces; + +interface MineRedisInterface +{ + /** + * 设置 key 类型名 + * @param string $typeName + */ + public function setTypeName(string $typeName): void; + + /** + * 获取key 类型名 + * @return string + */ + public function getTypeName(): string; +} \ No newline at end of file diff --git a/builder/Interfaces/UserServiceInterface.php b/builder/Interfaces/UserServiceInterface.php new file mode 100644 index 0000000..d3191f3 --- /dev/null +++ b/builder/Interfaces/UserServiceInterface.php @@ -0,0 +1,25 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ +namespace Builder\Interfaces; + +use Builder\Vo\UserServiceVo; + +/** + * 用户服务抽象 + */ +interface UserServiceInterface +{ + public function login(UserServiceVo $userServiceVo); + + public function logout(); +} \ No newline at end of file diff --git a/builder/Listener/DbQueryExecutedListener.php b/builder/Listener/DbQueryExecutedListener.php new file mode 100644 index 0000000..f03025a --- /dev/null +++ b/builder/Listener/DbQueryExecutedListener.php @@ -0,0 +1,65 @@ +logger = $container->get(LoggerFactory::class)->get('sql', 'sql'); + $this->console = $console; + } + + public function listen() : array + { + return [QueryExecuted::class]; + } + /** + * @param QueryExecuted $event + */ + public function process(object $event): void + { + if ($event instanceof QueryExecuted) { + $sql = $event->sql; + $offset = 0; + if (!Arr::isAssoc($event->bindings)) { + foreach ($event->bindings as $value) { + $value = is_array($value) ? json_encode($value) : "'{$value}'"; + $sql = Str::replaceFirst('?', "{$value}", $sql, $offset); + } + } + if (env('CONSOLE_SQL')) { + $this->console->info(sprintf('SQL[%s ms] %s ', $event->time, $sql)); + $this->logger->info(sprintf('[%s] %s', $event->time, $sql)); + } + } + } +} \ No newline at end of file diff --git a/builder/Listener/OperationListener.php b/builder/Listener/OperationListener.php new file mode 100644 index 0000000..0c9e67e --- /dev/null +++ b/builder/Listener/OperationListener.php @@ -0,0 +1,59 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ +declare (strict_types=1); +namespace Builder\Listener; +use App\System\Service\SystemOperLogService; +use Hyperf\Event\Annotation\Listener; +use Hyperf\Event\Contract\ListenerInterface; +use Psr\Container\ContainerInterface; +use Builder\Event\Operation; +/** + * Class OperationListener + * @package Builder\Listener + */ +#[Listener] +class OperationListener implements ListenerInterface +{ + + protected $container; + + protected $ignoreRouter = ['/login', '/getInfo', '/system/captcha']; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + /** + * @return string[] returns the events that you want to listen + */ + public function listen() : array + { + return [Operation::class]; + } + /** + * Handle the Event when the event is triggered, all listeners will + * complete before the event is returned to the EventDispatcher. + * @param object $event + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(object $event): void + { + $requestInfo = $event->getRequestInfo(); + if (!in_array($requestInfo['router'], $this->ignoreRouter)) { + $service = $this->container->get(SystemOperLogService::class); + $requestInfo['request_data'] = json_encode($requestInfo['request_data'], JSON_UNESCAPED_UNICODE); + // $requestInfo['response_data'] = $requestInfo['response_data']; + $service->save($requestInfo); + } + } +} \ No newline at end of file diff --git a/builder/Listener/ResumeExitCoordinatorListener.php b/builder/Listener/ResumeExitCoordinatorListener.php new file mode 100644 index 0000000..a4c70ce --- /dev/null +++ b/builder/Listener/ResumeExitCoordinatorListener.php @@ -0,0 +1,36 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Listener; + +use Hyperf\Command\Event\AfterExecute; +use Hyperf\Coordinator\Constants; +use Hyperf\Coordinator\CoordinatorManager; +use Hyperf\Event\Annotation\Listener; +use Hyperf\Event\Contract\ListenerInterface; + +#[Listener] +class ResumeExitCoordinatorListener implements ListenerInterface +{ + public function listen(): array + { + return [ + AfterExecute::class, + ]; + } + + public function process(object $event): void + { + CoordinatorManager::until(Constants::WORKER_EXIT)->resume(); + } +} \ No newline at end of file diff --git a/builder/Middlewares/CheckModuleMiddleware.php b/builder/Middlewares/CheckModuleMiddleware.php new file mode 100644 index 0000000..c9efba6 --- /dev/null +++ b/builder/Middlewares/CheckModuleMiddleware.php @@ -0,0 +1,73 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Middlewares; + +use App\System\Service\ModuleService; +use Hyperf\Di\Annotation\AnnotationCollector; +use Hyperf\Di\Annotation\Inject; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; + +use Builder\Helper\Str; +use Builder\Exception\NormalStatusException; + +/** + * 检查模块 + */ +class CheckModuleMiddleware implements MiddlewareInterface +{ + /** + * 模块服务 + * @var ModuleService + */ + #[Inject] + protected ModuleService $service; + + /** + * @param ServerRequestInterface $request + * @param RequestHandlerInterface $handler + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $uri = $request->getUri(); + + if ($uri->getPath() !== '/favicon.ico' && mb_substr_count($uri->getPath(), '/') > 1) { + + list($empty, $moduleName, $controllerName) = explode('/', $uri->getPath()); + + $path = $moduleName . '/' . $controllerName; + + $moduleName = Str::lower($moduleName); + + $module['enabled'] = false; + + foreach ($this->service->getModuleCache() as $name => $item) if (Str::lower($name) === $moduleName) { + $module = $item; + break; + } + + $annotation = AnnotationCollector::getClassesByAnnotation('Hyperf\HttpServer\Annotation\Controller'); + + foreach ($annotation as $item) if ( $item->server === 'http' && $item->prefix === $path && !$module['enabled']) { + throw new NormalStatusException('模块被禁用', 500); + } + } + + return $handler->handle($request); + } +} \ No newline at end of file diff --git a/builder/Middlewares/HttpCoreMiddleware.php b/builder/Middlewares/HttpCoreMiddleware.php new file mode 100644 index 0000000..f345f46 --- /dev/null +++ b/builder/Middlewares/HttpCoreMiddleware.php @@ -0,0 +1,65 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder\Middlewares; + +use Hyperf\HttpMessage\Stream\SwooleStream; +use Hyperf\Utils\Codec\Json; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Builder\Helper\MineCode; + +class HttpCoreMiddleware extends \Hyperf\HttpServer\CoreMiddleware +{ + /** + * Handle the response when cannot found any routes. + * @param ServerRequestInterface $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function handleNotFound(ServerRequestInterface $request): ResponseInterface + { + $format = [ + 'success' => false, + 'code' => MineCode::NOT_FOUND, + 'message' => t('mineadmin.not_found') + ]; + return $this->response()->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withStatus(404) + ->withBody(new SwooleStream(Json::encode($format))); + } + + /** + * Handle the response when the routes found but doesn't match any available methods. + * @param array $methods + * @param ServerRequestInterface $request + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function handleMethodNotAllowed( + array $methods, + ServerRequestInterface $request): ResponseInterface + { + $format = [ + 'success' => false, + 'code' => MineCode::METHOD_NOT_ALLOW, + 'message' => t('mineadmin.allow_method', ['method' => implode(',', $methods)]) + ]; + return $this->response()->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withStatus(405) + ->withBody(new SwooleStream(Json::encode($format))); + } +} \ No newline at end of file diff --git a/builder/Mine.php b/builder/Mine.php new file mode 100644 index 0000000..daa821b --- /dev/null +++ b/builder/Mine.php @@ -0,0 +1,197 @@ +setAppPath(BASE_PATH . '/app'); + $this->setAddonPath(BASE_PATH . '/addon'); + $this->scanModule(); + $this->scanAddon(); + } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function scanModule(): void + { + $modules = glob(self::getAppPath() . '*'); + $fs = container()->get(Filesystem::class); + $infos = []; + foreach ($modules as &$mod) if (is_dir($mod)) { + $modInfo = $mod . DIRECTORY_SEPARATOR . 'config.json'; + if (file_exists($modInfo)) { + $infos[basename($mod)] = json_decode($fs->sharedGet($modInfo), true); + } + } + $this->setModuleInfo($infos); + } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function scanAddon(): void + { + $modules = glob(self::getAddonPath() . '*'); + $fs = container()->get(Filesystem::class); + $infos = []; + foreach ($modules as &$mod) if (is_dir($mod)) { + $modInfo = $mod . DIRECTORY_SEPARATOR . 'config.json'; + if (file_exists($modInfo)) { + $infos[basename($mod)] = json_decode($fs->sharedGet($modInfo), true); + } + } + $this->setAddonInfo($infos); + } + + /** + * @return string + */ + public static function getVersion(): string + { + return self::$version; + } + + /** + * @return mixed + */ + public function getAppPath(): string + { + return $this->appPath . DIRECTORY_SEPARATOR; + } + + /** + * @return mixed + */ + public function getAddonPath(): string + { + return $this->addonPath . DIRECTORY_SEPARATOR; + } + + /** + * @param mixed $appPath + */ + public function setAppPath(string $appPath): void + { + $this->appPath = $appPath; + } + + /** + * @param mixed $appPath + */ + public function setAddonPath(string $path): void + { + $this->addonPath = $path; + } + + /** + * 获取模块信息 + * @param string|null $name + * @return mixed + */ + public function getModuleInfo(string $name = null): array + { + if (empty($name)) { + return $this->moduleInfo; + } + return $this->moduleInfo[$name] ?? []; + } + + /** + * 获取插件信息 + * @param string|null $name + * @return mixed + */ + public function getAddonInfo(string $name = null): array + { + if (empty($name)) { + return $this->AddonInfo; + } + return $this->AddonInfo[$name] ?? []; + } + + /** + * @param mixed $moduleInfo + */ + public function setModuleInfo($moduleInfo): void + { + $this->moduleInfo = $moduleInfo; + } + + /** + * @param mixed $AddonInfo + */ + public function setAddonInfo($AddonInfo): void + { + $this->AddonInfo = $AddonInfo; + } + /** + * @param String $key + * @param string $value + * @param false $save + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function setModuleConfigValue(String $key, string $value, bool $save = false): bool + { + if (strpos($key, '.') > 0) { + list($mod, $name) = explode('.', $key); + if (isset($this->moduleInfo[$mod]) && isset($this->moduleInfo[$mod][$name])) { + $this->moduleInfo[$mod][$name] = $value; + $save && $this->saveModuleConfig($mod); + return true; + } + } + return false; + } + /** + * @param string $mod + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function saveModuleConfig(string $mod): void + { + if (!empty($mod)) { + $fs = container()->get(Filesystem::class); + $modJson = $this->getAppPath() . $mod . DIRECTORY_SEPARATOR . 'config.json'; + if (! $fs->isWritable($modJson)) { + $fs->chmod($modJson, 666); + } + $fs->put($modJson, \json_encode($this->getModuleInfo($mod), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + } + } +} \ No newline at end of file diff --git a/builder/MineApi.php b/builder/MineApi.php new file mode 100644 index 0000000..a699e25 --- /dev/null +++ b/builder/MineApi.php @@ -0,0 +1,25 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Builder\Traits\ControllerTrait; + +/** + * API接口控制器基类 + * Class MineApi + * @package Mine + */ +abstract class MineApi +{ + use ControllerTrait; +} diff --git a/builder/MineCollection.php b/builder/MineCollection.php new file mode 100644 index 0000000..ecebe59 --- /dev/null +++ b/builder/MineCollection.php @@ -0,0 +1,133 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\Database\Model\Collection; +use Builder\Office\Excel\PhpOffice; +use Builder\Office\Excel\XlsWriter; + +class MineCollection extends Collection +{ + /** + * 系统菜单转前端路由树 + * @return array + */ + public function sysMenuToRouterTree(): array + { + $data = $this->toArray(); + if (empty($data)) return []; + + $routers = []; + foreach ($data as $menu) { + array_push($routers, $this->setRouter($menu)); + } + return $this->toTree($routers); + } + + /** + * @param $menu + * @return array + */ + public function setRouter(&$menu): array + { + $route = ($menu['type'] == 'L' || $menu['type'] == 'I') ? $menu['route'] : '/' . $menu['route']; + return [ + 'id' => $menu['id'], + 'parent_id' => $menu['parent_id'], + 'name' => $menu['code'], + 'component' => $menu['component'], + 'path' => $route, + 'redirect' => $menu['redirect'], + 'meta' => [ + 'type' => $menu['type'], + 'icon' => $menu['icon'], + 'title' => $menu['name'], + 'hidden' => ($menu['is_hidden'] === 1), + 'hiddenBreadcrumb' => false + ] + ]; + } + + /** + * @param array $data + * @param int $parentId + * @param string $id + * @param string $parentField + * @param string $children + * @return array + */ + public function toTree(array $data = [], int $parentId = 0, string $id = 'id', string $parentField = 'parent_id', string $children='children'): array + { + $data = $data ?: $this->toArray(); + + if (empty($data)) return []; + + $tree = []; + + foreach ($data as $value) { + if ($value[$parentField] == $parentId) { + $child = $this->toTree($data, $value[$id], $id, $parentField, $children); + if (!empty($child)) { + $value[$children] = $child; + } + array_push($tree, $value); + } + } + + unset($data); + return $tree; + } + + /** + * 导出数据 + * @param string $dto + * @param string $filename + * @param array|\Closure|null $closure + * @return \Psr\Http\Message\ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function export(string $dto, string $filename, array|\Closure $closure = null): \Psr\Http\Message\ResponseInterface + { + $excelDrive = config('mineadmin.excel_drive'); + if ($excelDrive === 'auto') { + $excel = extension_loaded('xlswriter') ? new XlsWriter($dto) : new PhpOffice($dto); + } else { + $excel = $excelDrive === 'xlsWriter' ? new XlsWriter($dto) : new PhpOffice($dto); + } + return $excel->export($filename, is_null($closure) ? $this->toArray() : $closure); + } + + /** + * 数据导入 + * @param string $dto + * @param MineModel $model + * @param \Closure|null $closure + * @return bool + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function import(string $dto, MineModel $model, ?\Closure $closure = null): bool + { + $excelDrive = config('mineadmin.excel_drive'); + if ($excelDrive === 'auto') { + $excel = extension_loaded('xlswriter') ? new XlsWriter($dto) : new PhpOffice($dto); + } else { + $excel = $excelDrive === 'xlsWriter' ? new XlsWriter($dto) : new PhpOffice($dto); + } + return $excel->import($model, $closure); + } + +} \ No newline at end of file diff --git a/builder/MineCommand.php b/builder/MineCommand.php new file mode 100644 index 0000000..142e702 --- /dev/null +++ b/builder/MineCommand.php @@ -0,0 +1,62 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\Command\Command as HyperfCommand; + +/** + * Class MineCommand + * @package System + */ +abstract class MineCommand extends HyperfCommand +{ + protected string $module; + + protected CONST CONSOLE_GREEN_BEGIN = "\033[32;5;1m"; + protected CONST CONSOLE_RED_BEGIN = "\033[31;5;1m"; + protected CONST CONSOLE_END = "\033[0m"; + + protected function getGreenText($text): string + { + return self::CONSOLE_GREEN_BEGIN . $text . self::CONSOLE_END; + } + + protected function getRedText($text): string + { + return self::CONSOLE_RED_BEGIN . $text . self::CONSOLE_END; + } + + protected function getStub($filename): string + { + return BASE_PATH . '/mine/Command/Creater/Stubs/' . $filename . '.stub'; + } + + protected function getModulePath(): string + { + return BASE_PATH . '/app/' . $this->module . '/Request/'; + } + + protected function getInfo(): string + { + return sprintf(' +/---------------------- welcome to use -----------------------\ +| _ ___ __ _ | +| ____ ___ (_)___ _____ / | ____/ /___ ___ (_)___ | +| / __ `__ \/ / __ \/ ___/ / /| |/ __ / __ `__ \/ / __ \ | +| / / / / / / / / / / /__/ / ___ / /_/ / / / / / / / / / / | +| /_/ /_/ /_/_/_/ /_/\___/ /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ | +| | +\_____________ Copyright MineAdmin 2021 ~ %s _____________| +', date('Y')); + } +} diff --git a/builder/MineController.php b/builder/MineController.php new file mode 100644 index 0000000..c47d03a --- /dev/null +++ b/builder/MineController.php @@ -0,0 +1,33 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\Di\Annotation\Inject; +use Psr\Container\ContainerInterface; +use Builder\Traits\ControllerTrait; + +/** + * 后台控制器基类 + * Class MineController + * @package Mine + */ +abstract class MineController +{ + use ControllerTrait; + + /** + * @var Mine + */ + #[Inject] + protected Mine $mine; +} diff --git a/builder/MineFormRequest.php b/builder/MineFormRequest.php new file mode 100644 index 0000000..02cf46e --- /dev/null +++ b/builder/MineFormRequest.php @@ -0,0 +1,62 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ +namespace Builder; + +use Hyperf\Validation\Request\FormRequest; + +class MineFormRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + */ + public function authorize(): bool + { + return true; + } + + /** + * 公共规则 + * @return array + */ + public function commonRules(): array + { + return []; + } + + /** + * Get the validation rules that apply to the request. + * @return array + */ + public function rules(): array + { + $operation = $this->getOperation(); + $method = $operation . 'Rules'; + $rules = ( $operation && method_exists($this, $method) ) ? $this->$method() : []; + return array_merge($rules, $this->commonRules()); + } + + /** + * @return string|null + */ + protected function getOperation(): ?string + { + $path = explode('/', $this->path()); + do { + $operation = array_pop($path); + } while (is_numeric($operation)); + + return $operation; + } + + +} \ No newline at end of file diff --git a/builder/MineModel.php b/builder/MineModel.php new file mode 100644 index 0000000..3b3f5fd --- /dev/null +++ b/builder/MineModel.php @@ -0,0 +1,102 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\DbConnection\Model\Model; +use Hyperf\ModelCache\Cacheable; +use Builder\Traits\ModelMacroTrait; + +/** + * Class MineModel + * @package Mine + */ +class MineModel extends Model +{ + use Cacheable, ModelMacroTrait; + + /** + * 隐藏的字段列表 + * @var string[] + */ + protected array $hidden = ['deleted_at']; + + /** + * 状态 + */ + public const ENABLE = 1; + public const DISABLE = 2; + + /** + * 默认每页记录数 + */ + public const PAGE_SIZE = 15; + + /** + * MineModel constructor. + * @param array $attributes + */ + public function __construct(array $attributes = []) + { + parent::__construct($attributes); + + //注册常用方法 + $this->registerBase(); + //注册用户数据权限方法 + $this->registerUserDataScope(); + } + + /** + * 设置主键的值 + * @param string | int $value + */ + public function setPrimaryKeyValue($value): void + { + $this->{$this->primaryKey} = $value; + } + + /** + * @return string + */ + public function getPrimaryKeyType(): string + { + return $this->keyType; + } + + /** + * @param array $options + * @return bool + */ + public function save(array $options = []): bool + { + return parent::save($options); + } + + /** + * @param array $attributes + * @param array $options + * @return bool + */ + public function update(array $attributes = [], array $options = []): bool + { + return parent::update($attributes, $options); + } + + /** + * @param array $models + * @return MineCollection + */ + public function newCollection(array $models = []): MineCollection + { + return new MineCollection($models); + } +} diff --git a/builder/MineModelVisitor.php b/builder/MineModelVisitor.php new file mode 100644 index 0000000..6bc6f16 --- /dev/null +++ b/builder/MineModelVisitor.php @@ -0,0 +1,69 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\Database\Commands\Ast\ModelUpdateVisitor as Visitor; +use Hyperf\Utils\Str; + +/** + * Class MineModelVisitor + * @package System + */ +class MineModelVisitor extends Visitor +{ + /** + * @param string $type + * @return string|null + */ + protected function formatDatabaseType(string $type): ?string + { + return match ($type) { + 'tinyint', 'smallint', 'mediumint', 'int', 'bigint' => 'integer', + 'decimal' => 'decimal:2', + 'float', 'double', 'real' => 'float', + 'bool', 'boolean' => 'boolean', + default => null, + }; + } + + /** + * @param string $type + * @param string|null $cast + * @return string|null + */ + protected function formatPropertyType(string $type, ?string $cast): ?string + { + if (! isset($cast)) { + $cast = $this->formatDatabaseType($type) ?? 'string'; + } + + switch ($cast) { + case 'integer': + return 'int'; + case 'date': + case 'datetime': + return '\Carbon\Carbon'; + case 'array': + return 'json'; + case 'json': + return 'array'; + } + + if (Str::startsWith($cast, 'decimal')) { + // 如果 cast 为 decimal,则 @property 改为 string + return 'string'; + } + + return $cast; + } +} \ No newline at end of file diff --git a/builder/MineRequest.php b/builder/MineRequest.php new file mode 100644 index 0000000..ddb53ab --- /dev/null +++ b/builder/MineRequest.php @@ -0,0 +1,59 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\Di\Annotation\Inject; +use Hyperf\HttpServer\Request; + +class MineRequest extends Request +{ + /** + * MineResponse + */ + #[Inject] + protected MineResponse $response; + + /** + * 获取请求IP + * @return string + */ + public function ip(): string + { + $ip = $this->getServerParams()['remote_addr'] ?? '0.0.0.0'; + $headers = $this->getHeaders(); + + if (isset($headers['x-real-ip'])) { + $ip = $headers['x-real-ip'][0]; + } else if (isset($headers['x-forwarded-for'])) { + $ip = $headers['x-forwarded-for'][0]; + } else if (isset($headers['http_x_forwarded_for'])) { + $ip = $headers['http_x_forwarded_for'][0]; + } + + return $ip; + } + + /** + * 获取协议架构 + * @return string + */ + public function getScheme(): string + { + if (isset($this->getHeader('X-scheme')[0])) { + return $this->getHeader('X-scheme')[0].'://'; + } else { + return 'http://'; + } + } +} + diff --git a/builder/MineResponse.php b/builder/MineResponse.php new file mode 100644 index 0000000..fb1f5b7 --- /dev/null +++ b/builder/MineResponse.php @@ -0,0 +1,97 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder; + +use Hyperf\HttpMessage\Stream\SwooleStream; +use Hyperf\HttpServer\Response; +use Psr\Http\Message\ResponseInterface; + +/** + * Class MineResponse + * @package MineServer + */ +class MineResponse extends Response +{ + /** + * @param string|null $message + * @param array|object $data + * @param int $code + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function success(string $message = null, array|object $data = [], int $code = 200): ResponseInterface + { + $format = [ + 'success' => true, + 'message' => $message ?: t('mineadmin.response_success'), + 'code' => $code, + 'data' => &$data, + ]; + $format = $this->toJson($format); + return $this->getResponse() + ->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withBody(new SwooleStream($format)); + } + + /** + * @param string $message + * @param int $code + * @param array $data + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function error(string $message = '', int $code = 500, array $data = []): ResponseInterface + { + $format = [ + 'success' => false, + 'code' => $code, + 'message' => $message ?: t('mineadmin.response_error'), + ]; + + if (!empty($data)) { + $format['data'] = &$data; + } + + $format = $this->toJson($format); + return $this->getResponse() + ->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', 'application/json; charset=utf-8') + ->withBody(new SwooleStream($format)); + } + + /** + * 向浏览器输出图片 + * @param string $image + * @param string $type + * @return ResponseInterface + */ + public function responseImage(string $image, string $type = 'image/png'): ResponseInterface + { + return $this->getResponse()->withHeader('Server', 'MineAdmin') + ->withAddedHeader('content-type', $type) + ->withBody(new SwooleStream($image)); + } + + /** + * @return ResponseInterface + */ + public function getResponse(): ResponseInterface + { + return parent::getResponse(); // TODO: Change the autogenerated stub + } +} + diff --git a/builder/MineServer.php b/builder/MineServer.php new file mode 100644 index 0000000..7603612 --- /dev/null +++ b/builder/MineServer.php @@ -0,0 +1,36 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; + +use Hyperf\HttpServer\Server; + +class MineServer extends Server +{ + protected ?string $serverName = 'MineAdmin'; + + protected $routes; + + public function onRequest($request, $response): void + { + parent::onRequest($request, $response); + $this->bootstrap(); + } + + /** + * MineServer bootstrap + * @return void + */ + protected function bootstrap(): void + { + } +} diff --git a/builder/MineStart.php b/builder/MineStart.php new file mode 100644 index 0000000..343b2bd --- /dev/null +++ b/builder/MineStart.php @@ -0,0 +1,46 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; +use App\System\Service\ModuleService; +use Hyperf\Framework\Bootstrap\ServerStartCallback; + +class MineStart extends ServerStartCallback +{ + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function beforeStart() + { + $service = container()->get(ModuleService::class); + $service->setModuleCache(); + $console = console(); + $console->info('MineAdmin start success...'); + $console->info($this->welcome()); + $console->info('current booting the user: ' . shell_exec('whoami')); + } + + protected function welcome(): string + { + return sprintf(' +/-------------- welcome to use -----------------\ +| _____ _ __ ___ | +| |_ _| | __ \ \ / (_) _____ __ | +| | | | |/ / \ \ / /| |/ _ \ \ /\ / / | +| | | | < \ V / | | __/\ V V / | +| |_| |_|\_\ \_/ |_|\___| \_/\_/ | +| | +\-------- Copyright ZoomTk 2023 ~ %s ---------- +', date('Y')); + } +} \ No newline at end of file diff --git a/builder/MineUpload.php b/builder/MineUpload.php new file mode 100644 index 0000000..637a951 --- /dev/null +++ b/builder/MineUpload.php @@ -0,0 +1,348 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); +namespace Builder; +use Hyperf\Di\Annotation\Inject; +use Hyperf\Filesystem\FilesystemFactory; +use Hyperf\HttpMessage\Upload\UploadedFile; +use League\Flysystem\Filesystem; +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Container\ContainerInterface; + +use App\System\Service\SettingConfigService; +use Builder\Exception\NormalStatusException; +use Builder\Helper\Str; +class MineUpload +{ + /** + * @var FilesystemFactory + */ + #[Inject] + protected FilesystemFactory $factory; + + /** + * @var Filesystem + */ + protected Filesystem $filesystem; + + /** + * @var EventDispatcherInterface + */ + #[Inject] + protected EventDispatcherInterface $evDispatcher; + + /** + * @var ContainerInterface + */ + protected ContainerInterface $container; + + /** + * MineUpload constructor. + * @param ContainerInterface $container + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + $this->filesystem = $this->factory->get($this->getMappingMode()); + } + + /** + * 获取文件操作处理系统 + * @return Filesystem + */ + public function getFileSystem(): Filesystem + { + return $this->filesystem; + } + + /** + * 上传文件 + * @param UploadedFile $uploadedFile + * @param array $config + * @return array + * @throws \League\Flysystem\FileExistsException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function upload(UploadedFile $uploadedFile, array $config = []): array + { + return $this->handleUpload($uploadedFile, $config); + } + + /** + * 处理上传 + * @param UploadedFile $uploadedFile + * @param array $config + * @return array + * @throws \League\Flysystem\FileExistsException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + protected function handleUpload(UploadedFile $uploadedFile, array $config): array + { + $tmpFile = $uploadedFile->getPath() . '/' . $uploadedFile->getFilename(); + $path = $this->getPath($config['path'] ?? null, $this->getStorageMode() != 1); + $filename = $this->getNewName() . '.' . Str::lower($uploadedFile->getExtension()); + + if (!$this->filesystem->writeStream($path . '/' . $filename, $uploadedFile->getStream()->detach())) { + throw new NormalStatusException((string) $uploadedFile->getError(), 500); + } + + $fileInfo = [ + 'storage_mode' => $this->getStorageMode(), + 'origin_name' => $uploadedFile->getClientFilename(), + 'object_name' => $filename, + 'mime_type' => $uploadedFile->getClientMediaType(), + 'storage_path' => $path, + 'hash' => md5_file($tmpFile), + 'suffix' => Str::lower($uploadedFile->getExtension()), + 'size_byte' => $uploadedFile->getSize(), + 'size_info' => format_size($uploadedFile->getSize() * 1024), + 'url' => $this->assembleUrl($config['path'] ?? null, $filename), + ]; + + $this->evDispatcher->dispatch(new \Builder\Event\UploadAfter($fileInfo)); + + return $fileInfo; + } + + /** + * 处理分块上传 + * @param array $data + * @return array + */ + public function handleChunkUpload(array $data): array + { + $uploadFile = $data['package']; + /* @var UploadedFile $uploadFile */ + $path = BASE_PATH . '/runtime/chunk/'; + $chunkName = "{$path}{$data['hash']}_{$data['total']}_{$data['index']}.chunk"; + $fs = container()->get(\Hyperf\Utils\Filesystem\Filesystem::class); + $fs->isDirectory($path) || $fs->makeDirectory($path); + $uploadFile->moveTo($chunkName); + if ($data['index'] === $data['total']) { + $content = ''; + for($i = 1; $i <= $data['total']; $i++) { + $chunkFile = "{$path}{$data['hash']}_{$data['total']}_{$i}.chunk"; + if (! $fs->isFile($chunkFile)) { + return ['chunk' => $data['index'], 'code' => 500, 'status' => 'fail']; + } + $content .= $fs->get($chunkFile); + $fs->delete($chunkFile); + } + $fileName = $this->getNewName().'.'.Str::lower($data['ext']); + $storagePath = $this->getPath(null, $this->getStorageMode() != 1); + if (! $this->filesystem->write($storagePath.'/'.$fileName, $content)) { + throw new NormalStatusException('分块上传失败', 500); + } + $fileInfo = [ + 'storage_mode' => $this->getStorageMode(), + 'origin_name' => $data['name'], + 'object_name' => $fileName, + 'mime_type' => $data['type'], + 'storage_path' => $storagePath, + 'hash' => $data['hash'], + 'suffix' => $data['ext'], + 'size_byte' => $data['size'], + 'size_info' => format_size(((int) $data['size'] * 1024)), + 'url' => $this->assembleUrl(null, $fileName), + ]; + + $this->evDispatcher->dispatch(new \Builder\Event\UploadAfter($fileInfo)); + + return $fileInfo; + } + return ['chunk' => $data['index'], 'code' => 201, 'status' => 'success']; + } + + /** + * 保存网络图片 + * @param array $data + * @return array + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + public function handleSaveNetworkImage(array $data): array + { + $path = $this->getPath($data['path'] ?? null, $this->getStorageMode() != 1); + $filename = $this->getNewName() . '.jpg'; + + try { + $content = file_get_contents($data['url']); + + $handle = fopen($data['url'], 'rb'); + $meta = stream_get_meta_data($handle); + fclose($handle); + + $dataInfo = $meta['wrapper_data']['headers'] ?? $meta['wrapper_data']; + $size = 0; + + foreach ($dataInfo as $va) { + if ( preg_match('/length/iU', $va) ) { + $ts = explode(':', $va); + $size = intval(trim(array_pop($ts))); + break; + } + } + + $realPath = BASE_PATH . '/runtime/' . $filename; + $fs = container()->get(\Hyperf\Utils\Filesystem\Filesystem::class); + $fs->put($realPath, $content); + + $hash = md5_file($realPath); + $fs->delete($realPath); + + if (! $hash) { + throw new \Exception(t('network_image_save_fail')); + } + + if ($model = (new \App\System\Mapper\SystemUploadFileMapper)->getFileInfoByHash($hash)) { + return $model->toArray(); + } + + if (!$this->filesystem->write($path . '/' . $filename, $content)) { + throw new \Exception(t('network_image_save_fail')); + } + + } catch (\Throwable $e) { + throw new NormalStatusException($e->getMessage(), 500); + } + + $fileInfo = [ + 'storage_mode' => $this->getStorageMode(), + 'origin_name' => md5((string) time()).'.jpg', + 'object_name' => $filename, + 'mime_type' => 'image/jpg', + 'storage_path' => $path, + 'suffix' => 'jpg', + 'hash' => $hash, + 'size_byte' => $size, + 'size_info' => format_size($size * 1024), + 'url' => $this->assembleUrl($data['path'] ?? null, $filename), + ]; + + $this->evDispatcher->dispatch(new \Builder\Event\UploadAfter($fileInfo)); + + return $fileInfo; + } + + /** + * @param string $config + * @param false $isContainRoot + * @return string + */ + protected function getPath(?string $path = null, bool $isContainRoot = false): string + { + $uploadfile = $isContainRoot ? '/'.env('UPLOAD_PATH', 'uploadfile').'/' : ''; + return empty($path) ? $uploadfile . date('Ymd') : $uploadfile . $path; + } + + /** + * 创建目录 + * @param string $name + * @return bool + */ + public function createUploadDir(string $name): bool + { + return $this->filesystem->createDir($name); + } + + /** + * 获取目录内容 + * @param string $path + * @return array + */ + public function listContents(string $path = ''): array + { + return $this->filesystem->listContents($path); + } + + /** + * 获取目录 + * @param string $path + * @param bool $isChildren + * @return array + */ + public function getDirectory(string $path, bool $isChildren): array + { + $contents = $this->filesystem->listContents($path, $isChildren); + $dirs = []; + foreach ($contents as $content) { + if ($content['type'] == 'dir') { + $dirs[] = $content; + } + } + return $dirs; + } + + /** + * 组装url + * @param string|null $path + * @param string $filename + * @return string + */ + public function assembleUrl(?string $path, string $filename): string + { + return $this->getPath($path, true) . '/' . $filename; + } + + /** + * 获取存储方式 + * @return int|string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getStorageMode(): int|string + { + return $this->container->get(SettingConfigService::class)->getConfigByKey('upload_mode')['value'] ?? 1; + } + + /** + * 获取编码后的文件名 + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function getNewName(): string + { + return (string) container()->get(\Hyperf\Snowflake\IdGeneratorInterface::class)->generate(); + } + + /** + * @return string + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function getMappingMode(): string + { + return match ( $this->getStorageMode() ) { + '1' => 'local', + '2' => 'oss', + '3' => 'qiniu', + '4' => 'cos', + default => 'local', + }; + } + + /** + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function getProtocol(): string + { + return $this->container->get(MineRequest::class)->getScheme(); + } +} \ No newline at end of file diff --git a/builder/Office/Excel/PhpOffice.php b/builder/Office/Excel/PhpOffice.php new file mode 100644 index 0000000..33bd437 --- /dev/null +++ b/builder/Office/Excel/PhpOffice.php @@ -0,0 +1,180 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Office\Excel; +use PhpOffice\PhpSpreadsheet\IOFactory; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Style\Color; + +use Builder\Exception\MineException; +use Builder\Office\ExcelPropertyInterface; +use Builder\Office\MineExcel; + +class PhpOffice extends MineExcel implements ExcelPropertyInterface +{ + + /** + * 导入 + * @param \Builder\MineModel $model + * @param \Closure|null $closure + * @return bool + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function import(\Builder\MineModel $model, ?\Closure $closure = null): bool + { + $request = container()->get(\Builder\MineRequest::class); + $data = []; + if ($request->hasFile('file')) { + $file = $request->file('file'); + $tempFileName = 'import_'.time().'.'.$file->getExtension(); + $tempFilePath = BASE_PATH . '/runtime/'. $tempFileName; + file_put_contents($tempFilePath, $file->getStream()->getContents()); + $reader = IOFactory::createReader(IOFactory::identify($tempFilePath)); + $reader->setReadDataOnly(true); + $sheet = $reader->load($tempFilePath); + $endCell = isset($this->property) ? chr(count($this->property) + 65) : null; + try { + foreach ($sheet->getActiveSheet()->getRowIterator(2) as $row) { + $temp = []; + foreach ($row->getCellIterator('A', $endCell) as $index => $item) { + $propertyIndex = ord($index) - 65; + if (isset($this->property[$propertyIndex])) { + $temp[$this->property[$propertyIndex]['name']] = $item->getFormattedValue(); + } + } + if (! empty($temp)) { + $data[] = $temp; + } + } + unlink($tempFilePath); + } catch (\Throwable $e) { + unlink($tempFilePath); + throw new MineException($e->getMessage()); + } + } else { + return false; + } + if ($closure instanceof \Closure) { + return $closure($model, $data); + } + + foreach ($data as $datum) { + $model::create($datum); + } + return true; + } + + /** + * 导出 + * @param string $filename + * @param array|\Closure $closure + * @return \Psr\Http\Message\ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function export(string $filename, array|\Closure $closure): \Psr\Http\Message\ResponseInterface + { + $spread = new Spreadsheet(); + $sheet = $spread->getActiveSheet(); + $filename .= '.xlsx'; + + is_array($closure) ? $data = &$closure : $data = $closure(); + + // 表头 + $titleStart = 'A'; + foreach ($this->property as $item) { + $headerColumn = $titleStart . '1'; + $sheet->setCellValue($headerColumn, $item['value']); + $style = $sheet->getStyle($headerColumn)->getFont()->setBold(true); + $columnDimension = $sheet->getColumnDimension($titleStart); + + empty($item['width']) ? $columnDimension->setAutoSize(true) : $columnDimension->setWidth((float) $item['width']); + + empty($item['align']) || $sheet->getStyle($headerColumn)->getAlignment()->setHorizontal($item['align']); + + empty($item['headColor']) || $style->setColor(new Color(str_replace('#', '', $item['headColor']))); + + if (!empty($item['headBgColor'])) { + $sheet->getStyle($headerColumn)->getFill() + ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) + ->getStartColor()->setARGB(str_replace('#', '', $item['headBgColor'])); + } + $titleStart++; + } + + $generate = $this->yieldExcelData($data); + + // 表体 + try { + $row = 2; + while ($generate->valid()) { + $column = 'A'; + foreach ($generate->current() as $name => $value) { + $columnRow = $column . $row; + $annotation = ''; + foreach ($this->property as $item) { + if ($item['name'] == $name) { + $annotation = $item; + break; + } + } + + if (!empty($annotation['dictName'])) { + $sheet->setCellValue($columnRow, $annotation['dictName'][$value]); + } else if (!empty($annotation['dictData'])) { + $sheet->setCellValue($columnRow, $annotation['dictData'][$value]); + } else { + $sheet->setCellValue($columnRow, $value . "\t"); + } + + if (! empty($item['color'])) { + $sheet->getStyle($columnRow)->getFont() + ->setColor(new Color(str_replace('#', '', $annotation['color']))); + } + + if (! empty($item['bgColor'])) { + $sheet->getStyle($columnRow)->getFill() + ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) + ->getStartColor()->setARGB(str_replace('#', '', $annotation['bgColor'])); + } + $column++; + } + $generate->next(); + $row++; + } + } catch (\RuntimeException $e) {} + + $writer = IOFactory::createWriter($spread, 'Xlsx'); + ob_start(); + $writer->save('php://output'); + $res = $this->downloadExcel($filename, ob_get_contents()); + ob_end_clean(); + $spread->disconnectWorksheets(); + + return $res; + } + + protected function yieldExcelData(array &$data): \Generator + { + foreach ($data as $dat) { + $yield = []; + foreach ($this->property as $item) { + $yield[ $item['name'] ] = $dat[$item['name']] ?? ''; + } + yield $yield; + } + } +} \ No newline at end of file diff --git a/builder/Office/Excel/XlsWriter.php b/builder/Office/Excel/XlsWriter.php new file mode 100644 index 0000000..be0c29b --- /dev/null +++ b/builder/Office/Excel/XlsWriter.php @@ -0,0 +1,165 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Office\Excel; +use Vtiful\Kernel\Format; +use Builder\Office\MineExcel; +use Builder\Exception\MineException; +use Builder\MineResponse; +use Builder\Office\ExcelPropertyInterface; + +class XlsWriter extends MineExcel implements ExcelPropertyInterface +{ + + /** + * 导入数据 + * @param \Builder\MineModel $model + * @param \Closure|null $closure + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + * @throws \Exception + */ + public function import(\Builder\MineModel $model, ?\Closure $closure = null): bool + { + $request = container()->get(\Builder\MineRequest::class); + if ($request->hasFile('file')) { + $file = $request->file('file'); + $tempFileName = 'import_'.time().'.'.$file->getExtension(); + $tempFilePath = BASE_PATH.'/runtime/'.$tempFileName; + file_put_contents($tempFilePath, $file->getStream()->getContents()); + $xlsxObject = new \Vtiful\Kernel\Excel(['path' => BASE_PATH . '/runtime/']); + $data = $xlsxObject->openFile($tempFileName)->openSheet()->getSheetData(); + unset($data[0]); + + $importData = []; + foreach ($data as $item) { + $tmp = []; + foreach ($item as $key => $value) { + $tmp[$this->property[$key]['name']] = (string) $value; + } + $importData[] = $tmp; + } + + if ($closure instanceof \Closure) { + return $closure($model, $importData); + } + + try { + foreach ($importData as $item) { + $model::create($item); + } + @unlink($tempFilePath); + } catch (\Exception $e) { + @unlink($tempFilePath); + throw new \Exception($e->getMessage()); + } + return true; + } else { + return false; + } + } + + /** + * 导出excel + * @param string $filename + * @param array|\Closure $closure + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function export(string $filename, array|\Closure $closure): \Psr\Http\Message\ResponseInterface + { + $filename .= '.xlsx'; + is_array($closure) ? $data = &$closure : $data = $closure(); + + $aligns = [ + 'left' => Format::FORMAT_ALIGN_LEFT, + 'center' => Format::FORMAT_ALIGN_CENTER, + 'right' => Format::FORMAT_ALIGN_RIGHT, + ]; + + $columnName = []; + $columnField = []; + foreach ($this->property as $item) { + $columnName[] = $item['value']; + $columnField[] = $item['name']; + } + + $tempFileName = 'export_' . time() . '.xlsx'; + $xlsxObject = new \Vtiful\Kernel\Excel(['path' => BASE_PATH . '/runtime/']); + $fileObject = $xlsxObject->fileName($tempFileName)->header($columnName); + $columnFormat = new Format($fileObject->getHandle()); + $rowFormat = new Format($fileObject->getHandle()); + + $index = 0; + for ($i = 65; $i < (65 + count($columnField)); $i++) { + $columnNumber = chr($i) . '1'; + $fileObject->setColumn( + sprintf('%s:%s', $columnNumber, $columnNumber), + $this->property[$index]['width'] ?? mb_strlen($columnName[$index]) * 5, + $columnFormat->align($this->property[$index]['align'] ? $aligns[$this->property[$index]['align']] : $aligns['left']) + ->background($this->property[$index]['bgColor'] ?? Format::COLOR_WHITE) + ->border(Format::BORDER_THIN) + ->fontColor($this->property[$index]['color'] ?? Format::COLOR_BLACK) + ->toResource() + ); + $index++; + } + + // 表头加样式 + $fileObject->setRow( + sprintf('A1:%s1', chr(65 + count($columnField))), 20, + $rowFormat->bold()->align(Format::FORMAT_ALIGN_CENTER, Format::FORMAT_ALIGN_VERTICAL_CENTER) + ->background(0x4ac1ff)->fontColor(Format::COLOR_BLACK) + ->border(Format::BORDER_THIN) + ->toResource() + ); + + $exportData = []; + foreach ($data as $item) { + $yield = []; + foreach ($this->property as $property) { + foreach ($item as $name => $value) { + if ($property['name'] == $name) { + if (!empty($property['dictName'])) { + $yield[] = $property['dictName'][$value]; + } else if (!empty($property['dictData'])) { + $yield[] = $property['dictData'][$value]; + } else { + $yield[] = $value; + } + break; + } + } + } + $exportData[] = $yield; + } + + $response = container()->get(MineResponse::class); + $filePath = $fileObject->data($exportData)->output(); + + $response->download($filePath, $filename); + + ob_start(); + if ( copy($filePath, 'php://output') === false) { + throw new MineException('导出数据失败', 500); + } + $res = $this->downloadExcel($filename, ob_get_contents()); + ob_end_clean(); + + @unlink($filePath); + + return $res; + } +} \ No newline at end of file diff --git a/builder/Office/ExcelPropertyInterface.php b/builder/Office/ExcelPropertyInterface.php new file mode 100644 index 0000000..329ea04 --- /dev/null +++ b/builder/Office/ExcelPropertyInterface.php @@ -0,0 +1,21 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Office; + +interface ExcelPropertyInterface +{ + public function import(\Builder\MineModel $model, ?\Closure $closure = null): bool; + + public function export(string $filename, array|\Closure $closure): \Psr\Http\Message\ResponseInterface; +} \ No newline at end of file diff --git a/builder/Office/MineExcel.php b/builder/Office/MineExcel.php new file mode 100644 index 0000000..802dead --- /dev/null +++ b/builder/Office/MineExcel.php @@ -0,0 +1,140 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Office; + +use App\System\Service\SystemDictDataService; +use Hyperf\Di\Annotation\AnnotationCollector; +use Hyperf\HttpMessage\Stream\SwooleStream; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +use Builder\Exception\MineException; +use Builder\Interfaces\MineModelExcel; +use Builder\MineResponse; + + +abstract class MineExcel +{ + public const ANNOTATION_NAME = 'Builder\Annotation\ExcelProperty'; + + /** + * @var array|null + */ + protected ?array $annotationMate; + + /** + * @var array + */ + protected array $property = []; + + + /** + * @param String $dto + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \RedisException + */ + public function __construct(string $dto) + { + if (!(new $dto) instanceof MineModelExcel) { + throw new MineException('dto does not implement an interface of the MineModelExcel', 500); + } + $this->annotationMate = AnnotationCollector::get($dto); + $this->parseProperty(); + } + + /** + * @return array + */ + public function getProperty(): array + { + return $this->property; + } + + /** + * @return array + */ + public function getAnnotationInfo(): array + { + return $this->annotationMate; + } + + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \RedisException + */ + protected function parseProperty(): void + { + if (empty($this->annotationMate) || !isset($this->annotationMate['_c'])) { + throw new MineException('dto annotation info is empty', 500); + } + + foreach ($this->annotationMate['_p'] as $name => $mate) { + $this->property[$mate[self::ANNOTATION_NAME]->index] = [ + 'name' => $name, + 'value' => $mate[self::ANNOTATION_NAME]->value, + 'width' => $mate[self::ANNOTATION_NAME]->width ?? null, + 'align' => $mate[self::ANNOTATION_NAME]->align ?? null, + 'headColor' => $mate[self::ANNOTATION_NAME]->headColor ?? null, + 'headBgColor' => $mate[self::ANNOTATION_NAME]->headBgColor ?? null, + 'color' => $mate[self::ANNOTATION_NAME]->color ?? null, + 'bgColor' => $mate[self::ANNOTATION_NAME]->bgColor ?? null, + 'dictData' => $mate[self::ANNOTATION_NAME]->dictData, + 'dictName' => empty($mate[self::ANNOTATION_NAME]->dictName) ? null : $this->getDictData($mate[self::ANNOTATION_NAME]->dictName), + ]; + } + + ksort($this->property); + } + + /** + * 下载excel + * @param string $filename + * @param string $content + * @return \Psr\Http\Message\ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + protected function downloadExcel(string $filename, string $content): \Psr\Http\Message\ResponseInterface + { + return container()->get(MineResponse::class)->getResponse() + ->withHeader('Server', 'MineAdmin') + ->withHeader('content-description', 'File Transfer') + ->withHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + ->withHeader('content-disposition', "attachment; filename={$filename}; filename*=UTF-8''" . rawurlencode($filename)) + ->withHeader('content-transfer-encoding', 'binary') + ->withHeader('pragma', 'public') + ->withBody(new SwooleStream($content)); + } + + /** + * 获取字典数据 + * @param string $dictName + * @return array + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \RedisException + */ + protected function getDictData(string $dictName): array + { + $data = []; + foreach (container()->get(SystemDictDataService::class)->getList(['code' => $dictName]) as $item) { + $data[$item['key']] = $item['title']; + } + + return $data; + } +} \ No newline at end of file diff --git a/builder/Redis/MineLockRedis.php b/builder/Redis/MineLockRedis.php new file mode 100644 index 0000000..55e64b0 --- /dev/null +++ b/builder/Redis/MineLockRedis.php @@ -0,0 +1,129 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Redis; + +use Hyperf\Utils\Coroutine; +use Builder\Abstracts\AbstractRedis; +use Builder\Exception\NormalStatusException; +use Builder\Interfaces\MineRedisInterface; + +class MineLockRedis extends AbstractRedis implements MineRedisInterface +{ + /** + * 设置 key 类型名 + * @param string $typeName + */ + public function setTypeName(string $typeName): void + { + $this->typeName = $typeName; + } + + /** + * 获取key 类型名 + * @return string + */ + public function getTypeName(): string + { + return $this->typeName; + } + + /** + * 运行锁,简单封装 + * @param \Closure $closure + * @param string $key + * @param int $expired + * @param int $timeout + * @param float $sleep + * @return bool + * @throws \Throwable + */ + public function run(\Closure $closure, string $key, int $expired, int $timeout = 0, float $sleep = 0.1): bool + { + if (! $this->lock($key, $expired, $timeout, $sleep)) { + return false; + } + + try { + call_user_func($closure); + } catch (\Throwable $e) { + logger('Redis Lock')->error(t('mineadmin.redis_lock_error')); + throw new NormalStatusException(t('mineadmin.redis_lock_error'), 500); + } finally { + $this->freed($key); + } + + return true; + } + + /** + * 检查锁 + * @param string $key + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function check(string $key): bool + { + return redis()->exists($this->getKey($key)); + } + + /** + * 添加锁 + * @param string $key + * @param int $expired + * @param int $timeout + * @param float $sleep + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function lock(string $key, int $expired, int $timeout = 0, float $sleep = 0.1): bool + { + $retry = $timeout > 0 ? intdiv($timeout * 100, 10) : 1; + + $name = $this->getKey($key); + + while ($retry > 0) { + + $lock = redis()->set($name, 1, ['nx', 'ex' => $expired]); + if ($lock || $timeout === 0) { + break; + } + Coroutine::id() ? Coroutine::sleep($sleep) : usleep(9999999); + + $retry--; + } + + return true; + } + + /** + * 释放锁 + * @param string $key + * @return bool + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function freed(string $key): bool + { + $luaScript = <<eval($luaScript, [$this->getKey($key), 1], 1) > 0; + } + +} \ No newline at end of file diff --git a/builder/Traits/ControllerTrait.php b/builder/Traits/ControllerTrait.php new file mode 100644 index 0000000..454fcb4 --- /dev/null +++ b/builder/Traits/ControllerTrait.php @@ -0,0 +1,90 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types = 1); + +namespace Builder\Traits; +use Hyperf\Di\Annotation\Inject; +use Psr\Http\Message\ResponseInterface; +use Builder\MineRequest; +use Builder\MineResponse; +trait ControllerTrait +{ + /** + * Mine 请求处理 + * MineRequest + */ + #[Inject] + protected MineRequest $request; + + /** + * Mine 响应处理 + * MineResponse + */ + #[Inject] + protected MineResponse $response; + + /** + * @param string|array|object $msgOrData + * @param array $data + * @param int $code + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function success(string|array|object $msgOrData = '', array|object $data = [], int $code = 200): ResponseInterface + { + if (is_string($msgOrData) || is_null($msgOrData)) { + return $this->response->success($msgOrData, $data, $code); + } else if (is_array($msgOrData) || is_object($msgOrData)) { + return $this->response->success(null, $msgOrData, $code); + } else { + return $this->response->success(null, $data, $code); + } + } + + /** + * @param string $message + * @param int $code + * @param array $data + * @return ResponseInterface + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function error(string $message = '', int $code = 500, array $data = []): ResponseInterface + { + return $this->response->error($message, $code, $data); + } + + /** + * 跳转 + * @param string $toUrl + * @param int $status + * @param string $schema + * @return ResponseInterface + */ + public function redirect(string $toUrl, int $status = 302, string $schema = 'http'): ResponseInterface + { + return $this->response->redirect($toUrl, $status, $schema); + } + + /** + * 下载文件 + * @param string $filePath + * @param string $name + * @return ResponseInterface + */ + public function _download(string $filePath, string $name = ''): ResponseInterface + { + return $this->response->download($filePath, $name); + } + +} \ No newline at end of file diff --git a/builder/Traits/MapperTrait.php b/builder/Traits/MapperTrait.php new file mode 100644 index 0000000..8eb18dc --- /dev/null +++ b/builder/Traits/MapperTrait.php @@ -0,0 +1,449 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Traits; + +use Hyperf\Contract\LengthAwarePaginatorInterface; +use Hyperf\Database\Model\Builder; +use Hyperf\Database\Model\Model; +use Builder\Annotation\Transaction; +use Builder\MineCollection; +use Builder\MineModel; + +trait MapperTrait +{ + /** + * @var MineModel + */ + public $model; + + /** + * 获取列表数据 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getList(?array $params, bool $isScope = true): array + { + return $this->listQuerySetting($params, $isScope)->get()->toArray(); + } + + /** + * 获取列表数据(带分页) + * @param array|null $params + * @param bool $isScope + * @param string $pageName + * @return array + */ + public function getPageList(?array $params, bool $isScope = true, string $pageName = 'page'): array + { + $paginate = $this->listQuerySetting($params, $isScope)->paginate( + $params['pageSize'] ?? $this->model::PAGE_SIZE, ['*'], $pageName, $params[$pageName] ?? 1 + ); + return $this->setPaginate($paginate); + } + + /** + * 设置数据库分页 + * @param LengthAwarePaginatorInterface $paginate + * @return array + */ + public function setPaginate(LengthAwarePaginatorInterface $paginate): array + { + return [ + 'items' => $paginate->items(), + 'pageInfo' => [ + 'total' => $paginate->total(), + 'currentPage' => $paginate->currentPage(), + 'totalPage' => $paginate->lastPage() + ] + ]; + } + + /** + * 获取树列表 + * @param array|null $params + * @param bool $isScope + * @param string $id + * @param string $parentField + * @param string $children + * @return array + */ + public function getTreeList( + ?array $params = null, + bool $isScope = true, + string $id = 'id', + string $parentField = 'parent_id', + string $children='children' + ): array + { + $params['_mineadmin_tree'] = true; + $params['_mineadmin_tree_pid'] = $parentField; + $data = $this->listQuerySetting($params, $isScope)->get(); + return $data->toTree([], $data[0]->{$parentField} ?? 0, $id, $parentField, $children); + } + + /** + * 返回模型查询构造器 + * @param array|null $params + * @param bool $isScope + * @return Builder + */ + public function listQuerySetting(?array $params, bool $isScope): Builder + { + $query = (($params['recycle'] ?? false) === true) ? $this->model::onlyTrashed() : $this->model::query(); + + if ($params['select'] ?? false) { + $query->select($this->filterQueryAttributes($params['select'])); + } + + $query = $this->handleOrder($query, $params); + + $isScope && $query->userDataScope(); + + return $this->handleSearch($query, $params); + } + + /** + * 排序处理器 + * @param Builder $query + * @param array|null $params + * @return Builder + */ + public function handleOrder(Builder $query, ?array &$params = null): Builder + { + // 对树型数据强行加个排序 + if (isset($params['_mineadmin_tree'])) { + $query->orderBy($params['_mineadmin_tree_pid']); + } + + if ($params['orderBy'] ?? false) { + if (is_array($params['orderBy'])) { + foreach ($params['orderBy'] as $key => $order) { + $query->orderBy($order, $params['orderType'][$key] ?? 'asc'); + } + } else { + $query->orderBy($params['orderBy'], $params['orderType'] ?? 'asc'); + } + } + + return $query; + } + + /** + * 搜索处理器 + * @param Builder $query + * @param array $params + * @return Builder + */ + public function handleSearch(Builder $query, array $params): Builder + { + return $query; + } + + /** + * 过滤查询字段不存在的属性 + * @param array $fields + * @param bool $removePk + * @return array + */ + protected function filterQueryAttributes(array $fields, bool $removePk = false): array + { + $model = new $this->model; + $attrs = $model->getFillable(); + foreach ($fields as $key => $field) { + if (!in_array(trim($field), $attrs) && mb_strpos(str_replace('AS', 'as', $field), 'as') === false) { + unset($fields[$key]); + } else { + $fields[$key] = trim($field); + } + } + if ($removePk && in_array($model->getKeyName(), $fields)) { + unset($fields[array_search($model->getKeyName(), $fields)]); + } + $model = null; + return ( count($fields) < 1 ) ? ['*'] : $fields; + } + + /** + * 过滤新增或写入不存在的字段 + * @param array $data + * @param bool $removePk + */ + protected function filterExecuteAttributes(array &$data, bool $removePk = false): void + { + $model = new $this->model; + $attrs = $model->getFillable(); + foreach ($data as $name => $val) { + if (!in_array($name, $attrs)) { + unset($data[$name]); + } + } + if ($removePk && isset($data[$model->getKeyName()])) { + unset($data[$model->getKeyName()]); + } + $model = null; + } + + /** + * 新增数据 + * @param array $data + * @return int + */ + public function save(array $data): int + { + $this->filterExecuteAttributes($data, $this->getModel()->incrementing); + $model = $this->model::create($data); + return $model->{$model->getKeyName()}; + } + + /** + * 读取一条数据 + * @param int $id + * @return MineModel|null + */ + public function read(int $id): ?MineModel + { + return ($model = $this->model::find($id)) ? $model : null; + } + + /** + * 按条件读取一行数据 + * @param array $condition + * @param array $column + * @return mixed + */ + public function first(array $condition, array $column = ['*']): ?MineModel + { + return ($model = $this->model::where($condition)->first($column)) ? $model : null; + } + + /** + * 获取单个值 + * @param array $condition + * @param string $columns + * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void|null + */ + public function value(array $condition, string $columns = 'id') + { + return ($model = $this->model::where($condition)->value($columns)) ? $model : null; + } + + /** + * 获取单列值 + * @param array $condition + * @param string $columns + * @return array + */ + public function pluck(array $condition, string $columns = 'id'): array + { + return $this->model::where($condition)->pluck($columns)->toArray(); + } + + /** + * 从回收站读取一条数据 + * @param int $id + * @return MineModel|null + * @noinspection PhpUnused + */ + public function readByRecycle(int $id): ?MineModel + { + return ($model = $this->model::withTrashed()->find($id)) ? $model : null; + } + + /** + * 单个或批量软删除数据 + * @param array $ids + * @return bool + */ + public function delete(array $ids): bool + { + $this->model::destroy($ids); + return true; + } + + /** + * 更新一条数据 + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + $this->filterExecuteAttributes($data, true); + return $this->model::find($id)->update($data) > 0; + } + + /** + * 按条件更新数据 + * @param array $condition + * @param array $data + * @return bool + */ + public function updateByCondition(array $condition, array $data): bool + { + $this->filterExecuteAttributes($data, true); + return $this->model::query()->where($condition)->update($data) > 0; + } + + /** + * 单个或批量真实删除数据 + * @param array $ids + * @return bool + */ + public function realDelete(array $ids): bool + { + foreach ($ids as $id) { + $model = $this->model::withTrashed()->find($id); + $model && $model->forceDelete(); + } + return true; + } + + /** + * 单个或批量从回收站恢复数据 + * @param array $ids + * @return bool + */ + public function recovery(array $ids): bool + { + $this->model::withTrashed()->whereIn((new $this->model)->getKeyName(), $ids)->restore(); + return true; + } + + /** + * 单个或批量禁用数据 + * @param array $ids + * @param string $field + * @return bool + */ + public function disable(array $ids, string $field = 'status'): bool + { + $this->model::query()->whereIn((new $this->model)->getKeyName(), $ids)->update([$field => $this->model::DISABLE]); + return true; + } + + /** + * 单个或批量启用数据 + * @param array $ids + * @param string $field + * @return bool + */ + public function enable(array $ids, string $field = 'status'): bool + { + $this->model::query()->whereIn((new $this->model)->getKeyName(), $ids)->update([$field => $this->model::ENABLE]); + return true; + } + + /** + * @return MineModel + */ + public function getModel(): MineModel + { + return new $this->model; + } + + /** + * 数据导入 + * @param string $dto + * @param \Closure|null $closure + * @return bool + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[Transaction] + public function import(string $dto, ?\Closure $closure = null): bool + { + return (new MineCollection())->import($dto, $this->getModel(), $closure); + } + + /** + * 闭包通用查询设置 + * @param \Closure|null $closure 传入的闭包查询 + * @return Builder + */ + public function settingClosure(?\Closure $closure = null): Builder + { + return $this->model::where(function($query) use($closure) { + if ($closure instanceof \Closure) { + $closure($query); + } + }); + } + + /** + * 闭包通用方式查询一条数据 + * @param \Closure|null $closure + * @param array|string[] $column + * @return Builder|Model|null + */ + public function one(?\Closure $closure = null, array $column = ['*']) + { + return $this->settingClosure($closure)->select($column)->first(); + } + + /** + * 闭包通用方式查询数据集合 + * @param \Closure|null $closure + * @param array|string[] $column + * @return array + */ + public function get(?\Closure $closure = null, array $column = ['*']): array + { + return $this->settingClosure($closure)->get($column)->toArray(); + } + + /** + * 闭包通用方式统计 + * @param \Closure|null $closure + * @param string $column + * @return int + */ + public function count(?\Closure $closure = null, string $column = '*'): int + { + return $this->settingClosure($closure)->count($column); + } + + /** + * 闭包通用方式查询最大值 + * @param \Closure|null $closure + * @param string $column + * @return mixed|string|void + */ + public function max(?\Closure $closure = null, string $column = '*') + { + return $this->settingClosure($closure)->max($column); + } + + /** + * 闭包通用方式查询最小值 + * @param \Closure|null $closure + * @param string $column + * @return mixed|string|void + */ + public function min(?\Closure $closure = null, string $column = '*') + { + return $this->settingClosure($closure)->min($column); + } + + /** + * 数字更新操作 + * @param int $id + * @param string $field + * @param int $value + * @return bool + */ + public function numberOperation(int $id, string $field, int $value): bool + { + return $this->update($id, [ $field => $value]); + } +} diff --git a/builder/Traits/ModelMacroTrait.php b/builder/Traits/ModelMacroTrait.php new file mode 100644 index 0000000..36d3995 --- /dev/null +++ b/builder/Traits/ModelMacroTrait.php @@ -0,0 +1,180 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +declare(strict_types=1); + +namespace Builder\Traits; + +use App\System\Model\SystemDept; +use App\System\Model\SystemRole; +use App\System\Model\SystemUser; +use Hyperf\Database\Model\Builder; +use Hyperf\DbConnection\Db; +use Builder\Exception\MineException; + +trait ModelMacroTrait +{ + /** + * 注册自定义方法 + */ + private function registerUserDataScope() + { + // 数据权限方法 + $model = $this; + Builder::macro('userDataScope', function(?int $userid = null) use($model) + { + if (! config('mineadmin.data_scope_enabled')) { + return $this; + } + + $userid = is_null($userid) ? (int) user()->getId() : $userid; + + if (empty($userid)) { + throw new MineException('Data Scope missing user_id'); + } + + /* @var Builder $this */ + if ($userid == env('SUPER_ADMIN')) { + return $this; + } + + if (!in_array('created_by', $model->getFillable())) { + return $this; + } + + $dataScope = new class($userid, $this) + { + // 用户ID + protected int $userid; + + // 查询构造器 + protected Builder $builder; + + // 数据范围用户ID列表 + protected array $userIds = []; + + public function __construct(int $userid, Builder $builder) + { + $this->userid = $userid; + $this->builder = $builder; + } + + /** + * @return Builder + */ + public function execute(): Builder + { + $this->getUserDataScope(); + return empty($this->userIds) + ? $this->builder + : $this->builder->whereIn('created_by', array_unique($this->userIds)); + } + + protected function getUserDataScope(): void + { + $userModel = SystemUser::find($this->userid, ['id']); + $roles = $userModel->roles()->get(['id', 'data_scope']); + + foreach ($roles as $role) { + switch ($role->data_scope) { + case SystemRole::ALL_SCOPE: + // 如果是所有权限,跳出所有循环 + break 2; + case SystemRole::CUSTOM_SCOPE: + // 自定义数据权限 + $deptIds = $role->depts()->pluck('id')->toArray(); + $this->userIds = array_merge( + $this->userIds, + Db::table('system_user_dept')->whereIn('dept_id', $deptIds)->pluck('user_id')->toArray() + ); + $this->userIds[] = $this->userid; + break; + case SystemRole::SELF_DEPT_SCOPE: + // 本部门数据权限 + $deptIds = Db::table('system_user_dept')->where('user_id', $userModel->id)->pluck('dept_id')->toArray(); + $this->userIds = array_merge( + $this->userIds, + Db::table('system_user_dept')->whereIn('dept_id', $deptIds)->pluck('user_id')->toArray() + ); + $this->userIds[] = $this->userid; + break; + case SystemRole::DEPT_BELOW_SCOPE: + // 本部门及子部门数据权限 + $parentDepts = Db::table('system_user_dept')->where('user_id', $userModel->id)->pluck('dept_id')->toArray(); + $ids = []; + foreach ($parentDepts as $deptId) { + $ids[] = SystemDept::query() + ->where(function ($query) use ($deptId) { + $query->where('id', '=', $deptId) + ->orWhere('level', 'like', $deptId . ',%') + ->orWhere('level', 'like', '%,' . $deptId); + }) + ->pluck('id') + ->toArray(); + } + $deptIds = array_merge($parentDepts, ...$ids); + $this->userIds = array_merge( + $this->userIds, + Db::table('system_user_dept')->whereIn('dept_id', $deptIds)->pluck('user_id')->toArray() + ); + $this->userIds[] = $this->userid; + break; + case SystemRole::SELF_SCOPE: + $this->userIds[] = $this->userid; + break; + default: + break; + } + } + } + }; + + return $dataScope->execute(); + }); + } + + /** + * Description:注册常用自定义方法 + * User:mike + */ + private function registerBase() + { + //添加andFilterWhere()方法 + Builder::macro('andFilterWhere', function ($key, $operator, $value = NULL) { + if ($value === '' || $value === '%%' || $value === '%') { + return $this; + } + if ($operator === '' || $operator === '%%' || $operator === '%') { + return $this; + } + if($value === NULL){ + return $this->where($key, $operator); + }else{ + return $this->where($key, $operator, $value); + } + }); + + //添加orFilterWhere()方法 + Builder::macro('orFilterWhere', function ($key, $operator, $value = NULL) { + if ($value === '' || $value === '%%' || $value === '%') { + return $this; + } + if ($operator === '' || $operator === '%%' || $operator === '%') { + return $this; + } + if($value === NULL){ + return $this->orWhere($key, $operator); + }else{ + return $this->orWhere($key, $operator, $value); + } + }); + } +} diff --git a/builder/Traits/ServiceTrait.php b/builder/Traits/ServiceTrait.php new file mode 100644 index 0000000..97e6612 --- /dev/null +++ b/builder/Traits/ServiceTrait.php @@ -0,0 +1,389 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +namespace Builder\Traits; + +use Hyperf\Database\Model\Collection; +use Psr\Http\Message\ResponseInterface; +use Builder\Abstracts\AbstractMapper; +use Builder\Annotation\Transaction; +use Builder\MineCollection; +use Builder\MineModel; +use Builder\MineResponse; + + +trait ServiceTrait +{ + /** + * @var AbstractMapper + */ + public $mapper; + + /** + * 获取列表数据 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getList(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = false; + return $this->mapper->getList($params, $isScope); + } + + /** + * 从回收站过去列表数据 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getListByRecycle(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = true; + return $this->mapper->getList($params, $isScope); + } + + /** + * 获取列表数据(带分页) + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getPageList(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + return $this->mapper->getPageList($params, $isScope); + } + + /** + * 从回收站获取列表数据(带分页) + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getPageListByRecycle(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = true; + return $this->mapper->getPageList($params, $isScope); + } + + /** + * 获取树列表 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeList(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = false; + return $this->mapper->getTreeList($params, $isScope); + } + + /** + * 从回收站获取树列表 + * @param array|null $params + * @param bool $isScope + * @return array + */ + public function getTreeListByRecycle(?array $params = null, bool $isScope = true): array + { + if ($params['select'] ?? null) { + $params['select'] = explode(',', $params['select']); + } + $params['recycle'] = true; + return $this->mapper->getTreeList($params, $isScope); + } + + /** + * 新增数据 + * @param array $data + * @return int + */ + public function save(array $data): int + { + return $this->mapper->save($data); + } + + /** + * 批量新增 + * @param array $collects + * @return bool + */ + #[Transaction] + public function batchSave(array $collects): bool + { + foreach ($collects as $collect) { + $this->mapper->save($collect); + } + return true; + } + + /** + * 读取一条数据 + * @param int $id + * @return MineModel|null + */ + public function read(int $id): ?MineModel + { + return $this->mapper->read($id); + } + + /** + * Description:获取单个值 + * User:mike + * @param array $condition + * @param string $columns + * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void|null + */ + public function value(array $condition, string $columns = 'id') + { + return $this->mapper->value($condition, $columns); + } + + /** + * Description:获取单列值 + * User:mike + * @param array $condition + * @param string $columns + * @return array|null + */ + public function pluck(array $condition, string $columns = 'id'): array + { + return $this->mapper->pluck($condition, $columns); + } + + /** + * 从回收站读取一条数据 + * @param int $id + * @return MineModel + * @noinspection PhpUnused + */ + public function readByRecycle(int $id): MineModel + { + return $this->mapper->readByRecycle($id); + } + + /** + * 单个或批量软删除数据 + * @param array $ids + * @return bool + */ + public function delete(array $ids): bool + { + return !empty($ids) && $this->mapper->delete($ids); + } + + /** + * 更新一条数据 + * @param int $id + * @param array $data + * @return bool + */ + public function update(int $id, array $data): bool + { + return $this->mapper->update($id, $data); + } + + /** + * 按条件更新数据 + * @param array $condition + * @param array $data + * @return bool + */ + public function updateByCondition(array $condition, array $data): bool + { + return $this->mapper->updateByCondition($condition, $data); + } + + /** + * 单个或批量真实删除数据 + * @param array $ids + * @return bool + */ + public function realDelete(array $ids): bool + { + return !empty($ids) && $this->mapper->realDelete($ids); + } + + /** + * 单个或批量从回收站恢复数据 + * @param array $ids + * @return bool + */ + public function recovery(array $ids): bool + { + return !empty($ids) && $this->mapper->recovery($ids); + } + + /** + * 单个或批量禁用数据 + * @param array $ids + * @param string $field + * @return bool + */ + public function disable(array $ids, string $field = 'status'): bool + { + return !empty($ids) && $this->mapper->disable($ids, $field); + } + + /** + * 单个或批量启用数据 + * @param array $ids + * @param string $field + * @return bool + */ + public function enable(array $ids, string $field = 'status'): bool + { + return !empty($ids) && $this->mapper->enable($ids, $field); + } + + /** + * 修改数据状态 + * @param int $id + * @param string $value + * @param string $filed + * @return bool + */ + public function changeStatus(int $id, string $value, string $filed = 'status'): bool + { + return $value == MineModel::ENABLE ? $this->mapper->enable([ $id ], $filed) : $this->mapper->disable([ $id ], $filed); + } + + /** + * 数字更新操作 + * @param int $id + * @param string $field + * @param int $value + * @return bool + */ + public function numberOperation(int $id, string $field, int $value): bool + { + return $this->mapper->numberOperation($id, $field, $value); + } + + /** + * 导出数据 + * @param array $params + * @param string|null $dto + * @param string|null $filename + * @return ResponseInterface + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function export(array $params, ?string $dto, string $filename = null): ResponseInterface + { + if (empty($dto)) { + return container()->get(MineResponse::class)->error('导出未指定DTO'); + } + + if (empty($filename)) { + $filename = $this->mapper->getModel()->getTable(); + } + + return (new MineCollection())->export($dto, $filename, $this->mapper->getList($params)); + } + + /** + * 数据导入 + * @param string $dto + * @param \Closure|null $closure + * @return bool + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + #[Transaction] + public function import(string $dto, ?\Closure $closure = null): bool + { + return $this->mapper->import($dto, $closure); + } + + /** + * 数组数据转分页数据显示 + * @param array|null $params + * @param string $pageName + * @return array + */ + public function getArrayToPageList(?array $params = [], string $pageName = 'page'): array + { + $collect = $this->handleArraySearch(collect($this->getArrayData($params)), $params); + + $pageSize = MineModel::PAGE_SIZE; + $page = 1; + + if ($params[$pageName] ?? false) { + $page = (int) $params[$pageName]; + } + + if ($params['pageSize'] ?? false) { + $pageSize = (int) $params['pageSize']; + } + + $data = $collect->forPage($page, $pageSize)->toArray(); + + return [ + 'items' => $this->getCurrentArrayPageBefore($data, $params), + 'pageInfo' => [ + 'total' => $collect->count(), + 'currentPage' => $page, + 'totalPage' => ceil($collect->count() / $pageSize) + ] + ]; + } + + /** + * 数组数据搜索器 + * @param \Hyperf\Utils\Collection $collect + * @param array $params + * @return Collection + */ + protected function handleArraySearch(\Hyperf\Utils\Collection $collect, array $params): \Hyperf\Utils\Collection + { + return $collect; + } + + /** + * 数组当前页数据返回之前处理器,默认对key重置 + * @param array $data + * @param array $params + * @return array + */ + protected function getCurrentArrayPageBefore(array &$data, array $params = []): array + { + sort($data); + return $data; + } + + /** + * 设置需要分页的数组数据 + * @param array $params + * @return array + */ + protected function getArrayData(array $params = []): array + { + return []; + } +} diff --git a/builder/View.php b/builder/View.php new file mode 100644 index 0000000..2e43a2e --- /dev/null +++ b/builder/View.php @@ -0,0 +1,79 @@ +getApiRoot(); + $homeUrl = $setting->getHomeUrl(); + $token = $setting->getUser()->getToken(); + $title = $setting->getTitle(); + if ($setting->getUser()->getUid() > 0) { + $root = 'root'; + } else { + $root = 'login'; + } + $pageData = $setting->toArray(); + $pageData = json_encode($pageData, 256); + if (!$content) { + $content = << + + + + + {$title} + + + +
{{ message }}
+ + + + + +EOF; + } + $response = new Response(); + $request = $response->withHeader('content-type', 'text/html; charset=utf8') + ->withBody(new SwooleStream($content)); + return $request; + } + +} \ No newline at end of file diff --git a/builder/Vo/UserServiceVo.php b/builder/Vo/UserServiceVo.php new file mode 100644 index 0000000..ca8839a --- /dev/null +++ b/builder/Vo/UserServiceVo.php @@ -0,0 +1,136 @@ +username; + } + + /** + * @param string $username + */ + public function setUsername(string $username): void + { + $this->username = $username; + } + + /** + * @return string + */ + public function getPassword(): string + { + return $this->password; + } + + /** + * @param string $password + */ + public function setPassword(string $password): void + { + $this->password = $password; + } + + /** + * @return string + */ + public function getPhone(): string + { + return $this->phone; + } + + /** + * @param string $phone + */ + public function setPhone(string $phone): void + { + $this->phone = $phone; + } + + /** + * @return string + */ + public function getEmail(): string + { + return $this->email; + } + + /** + * @param string $email + */ + public function setEmail(string $email): void + { + $this->email = $email; + } + + /** + * @return string + */ + public function getVerifyCode(): string + { + return $this->verifyCode; + } + + /** + * @param string $verifyCode + */ + public function setVerifyCode(string $verifyCode): void + { + $this->verifyCode = $verifyCode; + } + + /** + * @return array + */ + public function getOther(): array + { + return $this->other; + } + + /** + * @param array $other + */ + public function setOther(array $other): void + { + $this->other = $other; + } +} \ No newline at end of file diff --git a/common/common.php b/common/common.php new file mode 100644 index 0000000..9b058aa --- /dev/null +++ b/common/common.php @@ -0,0 +1,2 @@ +=8.0", + "ext-gd": "*", + "ext-json": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "ext-redis": "*", + "ext-swoole": ">=4.5", + "doctrine/dbal": "^3.1", + "easyswoole/verifycode": "3.x", + "hyperf/amqp": "3.0.*", + "hyperf/async-queue": "3.0.*", + "hyperf/cache": "3.0.*", + "hyperf/command": "3.0.*", + "hyperf/config": "3.0.*", + "hyperf/crontab": "3.0.*", + "hyperf/database": "3.0.*", + "hyperf/db-connection": "3.0.*", + "hyperf/filesystem": "3.0.*", + "hyperf/framework": "3.0.*", + "hyperf/guzzle": "3.0.*", + "hyperf/http-server": "3.0.*", + "hyperf/logger": "3.0.*", + "hyperf/memory": "3.0.*", + "hyperf/model-cache": "3.0.*", + "hyperf/paginator": "3.0.*", + "hyperf/pool": "3.0.*", + "hyperf/process": "3.0.*", + "hyperf/redis": "3.0.*", + "hyperf/snowflake": "3.0.*", + "hyperf/translation": "3.0.*", + "hyperf/validation": "3.0.*", + "hyperf/websocket-server": "3.0.*", + "overtrue/flysystem-cos": "^3.0", + "overtrue/flysystem-qiniu": "^1.0", + "phpoffice/phpspreadsheet": "^1.24", + "symfony/property-access": "^6.0", + "tangwei/apidocs": "^2.1", + "xmo/jwt-auth": "0.4.*", + "xxtime/flysystem-aliyun-oss": "^1.5", + "yurunsoft/phpmailer-swoole": "^1.0", + "zoujingli/ip2region": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.9", + "hyperf/devtool": "3.0.*", + "hyperf/ide-helper": "3.0.*", + "hyperf/testing": "3.0.*", + "hyperf/watcher": "3.0.*", + "mockery/mockery": "^1.0", + "phpstan/phpstan": "^0.12", + "swoole/ide-helper": "^4.5" + }, + "suggest": { + "ext-openssl": "Required to use HTTPS.", + "ext-json": "Required to use JSON.", + "ext-pdo": "Required to use MySQL Client.", + "ext-pdo_mysql": "Required to use MySQL Client.", + "ext-redis": "Required to use Redis Client." + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Builder\\": "builder/", + "Api\\": "api/", + "Addon\\": "addon/" + }, + "files": [ + "builder/Helper/functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "HyperfTest\\": "./test/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "optimize-autoloader": true, + "sort-packages": true, + "allow-plugins": { + "composer/package-versions-deprecated": true + } + }, + "extra": [], + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-autoload-dump": [ + "rm -rf runtime/container" + ], + "test": "co-phpunit --prepend test/bootstrap.php -c phpunit.xml --colors=always", + "cs-fix": "php-cs-fixer fix $1", + "analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config ./mine", + "start": [ + "Composer\\Config::disableProcessTimeout", + "php ./bin/hyperf.php start" + ] + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://mirrors.aliyun.com/composer/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..ac286be --- /dev/null +++ b/composer.lock @@ -0,0 +1,11956 @@ +{ + "_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": "3551f1700ee401f5177d46141babdd68", + "packages": [ + { + "name": "aliyuncs/oss-sdk-php", + "version": "v2.6.0", + "source": { + "type": "git", + "url": "https://github.com/aliyun/aliyun-oss-php-sdk.git", + "reference": "572d0f8e099e8630ae7139ed3fdedb926c7a760f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/572d0f8e099e8630ae7139ed3fdedb926c7a760f", + "reference": "572d0f8e099e8630ae7139ed3fdedb926c7a760f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "*", + "satooshi/php-coveralls": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "OSS\\": "src/OSS" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aliyuncs", + "homepage": "http://www.aliyun.com" + } + ], + "description": "Aliyun OSS SDK for PHP", + "homepage": "http://www.aliyun.com/product/oss/", + "support": { + "issues": "https://github.com/aliyun/aliyun-oss-php-sdk/issues", + "source": "https://github.com/aliyun/aliyun-oss-php-sdk/tree/v2.6.0" + }, + "time": "2022-08-03T08:06:01+00:00" + }, + { + "name": "doctrine/annotations", + "version": "1.14.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/ad785217c1e9555a7d6c6c8c9f406395a5e2882b", + "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/lexer": "^1 || ^2", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "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.14.2" + }, + "time": "2022-12-15T06:48:22+00:00" + }, + { + "name": "doctrine/cache", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "~7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "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": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", + "keywords": [ + "abstraction", + "apcu", + "cache", + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" + ], + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "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%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" + }, + { + "name": "doctrine/dbal", + "version": "3.5.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "88fa7e5189fd5ec6682477044264dc0ed4e3aa1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/88fa7e5189fd5ec6682477044264dc0ed4e3aa1e", + "reference": "88fa7e5189fd5ec6682477044264dc0ed4e3aa1e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "doctrine/coding-standard": "11.0.0", + "jetbrains/phpstorm-stubs": "2022.3", + "phpstan/phpstan": "1.9.4", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "9.5.27", + "psalm/plugin-phpunit": "0.18.4", + "squizlabs/php_codesniffer": "3.7.1", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.30.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\": "src" + } + }, + "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" + } + ], + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "keywords": [ + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.5.3" + }, + "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%2Fdbal", + "type": "tidelift" + } + ], + "time": "2023-01-12T10:21:44+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", + "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "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" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/1.1.x" + }, + "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%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2020-05-29T18:28:51+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "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": "^8.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "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": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.4" + }, + "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%2Finflector", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:16:43+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "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%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "doctrine/lexer", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "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/2.1.0" + }, + "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": "2022-12-14T08:49:07+00:00" + }, + { + "name": "easyswoole/spl", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/easy-swoole/spl.git", + "reference": "3d02bceaf1031a78f959d41dab04e37121f9c58c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/easy-swoole/spl/zipball/3d02bceaf1031a78f959d41dab04e37121f9c58c", + "reference": "3d02bceaf1031a78f959d41dab04e37121f9c58c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "php": ">=7.1.0" + }, + "require-dev": { + "easyswoole/phpunit": "^1.0", + "easyswoole/swoole-ide-helper": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "EasySwoole\\Spl\\": "src/", + "EasySwoole\\Spl\\Test\\": "test/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "YF", + "email": "291323003@qq.com" + } + ], + "description": "php stander lib", + "homepage": "https://www.easyswoole.com/", + "keywords": [ + "async", + "easyswoole", + "framework", + "swoole" + ], + "support": { + "issues": "https://github.com/easy-swoole/spl/issues", + "source": "https://github.com/easy-swoole/spl/tree/1.4.1" + }, + "time": "2022-02-02T12:04:48+00:00" + }, + { + "name": "easyswoole/verifycode", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/easy-swoole/verify-code.git", + "reference": "eccd312bef30cf46169e82527d869babdaeabdcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/easy-swoole/verify-code/zipball/eccd312bef30cf46169e82527d869babdaeabdcf", + "reference": "eccd312bef30cf46169e82527d869babdaeabdcf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "easyswoole/spl": "^1.0", + "ext-gd": "*", + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "EasySwoole\\VerifyCode\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "YF", + "email": "291323003@qq.com" + }, + { + "name": "evalor", + "email": "mipone@foxmail.com" + } + ], + "support": { + "issues": "https://github.com/easy-swoole/verify-code/issues", + "source": "https://github.com/easy-swoole/verify-code/tree/3.1.0" + }, + "time": "2023-01-30T08:39:06+00:00" + }, + { + "name": "egulias/email-validator", + "version": "3.2.5", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "b531a2311709443320c786feb4519cfaf94af796" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b531a2311709443320c786feb4519cfaf94af796", + "reference": "b531a2311709443320c786feb4519cfaf94af796", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/lexer": "^1.2|^2", + "php": ">=7.2", + "symfony/polyfill-intl-idn": "^1.15" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.8|^9.3.3", + "vimeo/psalm": "^4" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.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/3.2.5" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-01-02T17:26:14+00:00" + }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.16.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8", + "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0" + }, + "time": "2022-09-18T07:06:19+00:00" + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.9 || ^2.4", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "7.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-08-28T15:39:27+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "b94b2807d85443f9719887892882d0329d1e2598" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", + "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2022-08-28T14:55:35+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.4.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "67c26b443f348a51926030c83481b85718457d3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", + "reference": "67c26b443f348a51926030c83481b85718457d3d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.4.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-10-26T14:07:24+00:00" + }, + { + "name": "hyperf/amqp", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/hyperf/amqp.git", + "reference": "87b2a2081be8e58c6dad377360f033361a2742ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/amqp/zipball/87b2a2081be8e58c6dad377360f033361a2742ea", + "reference": "87b2a2081be8e58c6dad377360f033361a2742ea", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/instantiator": "^1.2.0", + "hyperf/contract": "~3.0.0", + "hyperf/pool": "~3.0.0", + "hyperf/process": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "php-amqplib/php-amqplib": "3.4.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.0|^2.0|^3.0" + }, + "suggest": { + "hyperf/di": "Required to use annotations.", + "hyperf/event": "Declare queue and start consumers automatically." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Amqp\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Amqp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A amqplib for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "AMQP", + "hyperf", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-17T09:54:51+00:00" + }, + { + "name": "hyperf/async-queue", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/async-queue.git", + "reference": "a436290438d5767ed78b9448cf2fc130caf818b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/async-queue/zipball/a436290438d5767ed78b9448cf2fc130caf818b3", + "reference": "a436290438d5767ed78b9448cf2fc130caf818b3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/command": "~3.0.0", + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0" + }, + "suggest": { + "hyperf/di": "Required to use annotations.", + "hyperf/event": "Required to dispatch a event.", + "hyperf/logger": "Required to use QueueHandleListener.", + "hyperf/process": "Auto register the consumer process for server." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\AsyncQueue\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\AsyncQueue\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A async queue component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "async-queue", + "hyperf", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-16T02:16:48+00:00" + }, + { + "name": "hyperf/cache", + "version": "v3.0.0-rc.17", + "source": { + "type": "git", + "url": "https://github.com/hyperf/cache.git", + "reference": "bba789fc11d7ca2a20ae76cf87d9476be2011344" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/cache/zipball/bba789fc11d7ca2a20ae76cf87d9476be2011344", + "reference": "bba789fc11d7ca2a20ae76cf87d9476be2011344", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "suggest": { + "hyperf/di": "Use cache annotations.", + "hyperf/event": "Use listener to delete annotation cache." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Cache\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A cache component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "cache", + "hyperf", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-22T04:45:19+00:00" + }, + { + "name": "hyperf/command", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/command.git", + "reference": "3bb67fd9e776b2987e2b2daf47f55987ad322122" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/command/zipball/3bb67fd9e776b2987e2b2daf47f55987ad322122", + "reference": "3bb67fd9e776b2987e2b2daf47f55987ad322122", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/event-dispatcher": "^1.0", + "symfony/console": "^5.0|^6.0" + }, + "suggest": { + "hyperf/di": "Required to use annotations." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Command\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Command for hyperf", + "keywords": [ + "command", + "php", + "swoole" + ], + "support": { + "issues": "https://github.com/hyperf/command/issues", + "source": "https://github.com/hyperf/command/tree/v3.0.0" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/config", + "version": "v3.0.0-rc.8", + "source": { + "type": "git", + "url": "https://github.com/hyperf/config.git", + "reference": "dbab570287af7b7d79967446b7f4703c4ffaba8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/config/zipball/dbab570287af7b7d79967446b7f4703c4ffaba8e", + "reference": "dbab570287af7b7d79967446b7f4703c4ffaba8e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "symfony/finder": "^5.0|^6.0" + }, + "suggest": { + "hyperf/di": "Allows using @Value annotation", + "hyperf/event": "Allows using @Value annotation", + "hyperf/framework": "Allows using @Value annotation", + "vlucas/phpdotenv": "Allows using enviroment value to override the config" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Config\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "./src/Functions.php" + ], + "psr-4": { + "Hyperf\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An independent component that provides configuration container.", + "homepage": "https://hyperf.io", + "keywords": [ + "config", + "configuration", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-08-30T06:33:36+00:00" + }, + { + "name": "hyperf/context", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/context.git", + "reference": "95cd634ece12f1f4898d10815060090191d59527" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/context/zipball/95cd634ece12f1f4898d10815060090191d59527", + "reference": "95cd634ece12f1f4898d10815060090191d59527", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/engine": "^1.2|^2.0", + "php": ">=8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Context\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A coroutine context library.", + "homepage": "https://hyperf.io", + "keywords": [ + "Context", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/contract", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/contract.git", + "reference": "ec465d74f94341ff9b979cc4a3f0e2ededc05040" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/contract/zipball/ec465d74f94341ff9b979cc4a3f0e2ededc05040", + "reference": "ec465d74f94341ff9b979cc4a3f0e2ededc05040", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Contract\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The contracts of Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/coordinator", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/hyperf/coordinator.git", + "reference": "df75218d463af1acd98cae9e8f15675b1b64dfee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/coordinator/zipball/df75218d463af1acd98cae9e8f15675b1b64dfee", + "reference": "df75218d463af1acd98cae9e8f15675b1b64dfee", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/engine": "^1.2|^2.0", + "php": ">=8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Coordinator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Hyperf Coordinator", + "homepage": "https://hyperf.io", + "keywords": [ + "Coordinator", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-11T04:01:35+00:00" + }, + { + "name": "hyperf/crontab", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/crontab.git", + "reference": "3ba0792038cc0b27d120e1d92f0b351121841789" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/crontab/zipball/3ba0792038cc0b27d120e1d92f0b351121841789", + "reference": "3ba0792038cc0b27d120e1d92f0b351121841789", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/utils": "~3.0.0", + "nesbot/carbon": "^2.0", + "php": ">=8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Crontab\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Crontab\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A crontab component for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "crontab", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-12-10T13:24:37+00:00" + }, + { + "name": "hyperf/database", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/hyperf/database.git", + "reference": "8b5ef59cfa6b3aec62356e60246dc77630f5a30a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/database/zipball/8b5ef59cfa6b3aec62356e60246dc77630f5a30a", + "reference": "8b5ef59cfa6b3aec62356e60246dc77630f5a30a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/macroable": "~3.0.0", + "hyperf/utils": "~3.0.0", + "nesbot/carbon": "^2.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0" + }, + "suggest": { + "doctrine/dbal": "Required to rename columns (^3.0).", + "nikic/php-parser": "Required to use ModelCommand. (^4.0)", + "php-di/phpdoc-reader": "Required to use ModelCommand. (^2.2)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Database\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A flexible database library.", + "homepage": "https://hyperf.io", + "keywords": [ + "database", + "hyperf", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-12T10:04:57+00:00" + }, + { + "name": "hyperf/db-connection", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/hyperf/db-connection.git", + "reference": "88aa0a30700ad9541f946bf933105900ac813215" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/db-connection/zipball/88aa0a30700ad9541f946bf933105900ac813215", + "reference": "88aa0a30700ad9541f946bf933105900ac813215", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/database": "~3.0.0", + "hyperf/di": "~3.0.0", + "hyperf/framework": "~3.0.0", + "hyperf/model-listener": "~3.0.0", + "hyperf/pool": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\DbConnection\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\DbConnection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A hyperf db connection handler for hyperf/database.", + "homepage": "https://hyperf.io", + "keywords": [ + "Connection", + "database", + "hyperf", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-11T10:01:35+00:00" + }, + { + "name": "hyperf/di", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/di.git", + "reference": "1cb7161eb6366d0c23f75eee749ca29286f2bd6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/di/zipball/1cb7161eb6366d0c23f75eee749ca29286f2bd6e", + "reference": "1cb7161eb6366d0c23f75eee749ca29286f2bd6e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/instantiator": "^1.0", + "nikic/php-parser": "^4.1", + "php": ">=8.0", + "php-di/phpdoc-reader": "^2.2", + "psr/container": "^1.0|^2.0", + "symfony/finder": "^5.0|^6.0", + "vlucas/phpdotenv": "^5.0" + }, + "suggest": { + "ext-pcntl": "Required to scan annotations.", + "hyperf/config": "Require this component for annotation scan progress to retrieve the scan path." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Di\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Di\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A DI for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "annotation", + "di", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-01T05:13:29+00:00" + }, + { + "name": "hyperf/dispatcher", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/dispatcher.git", + "reference": "f506c5102583d69b7225fc7045ee707a65d2b4d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/dispatcher/zipball/f506c5102583d69b7225fc7045ee707a65d2b4d0", + "reference": "f506c5102583d69b7225fc7045ee707a65d2b4d0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Dispatcher\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Dispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A HTTP Server for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "dispatcher", + "filter", + "hyperf", + "middleware", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-08-30T06:33:36+00:00" + }, + { + "name": "hyperf/engine", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/hyperf/engine.git", + "reference": "e512298a2079acb824c9cad3984344f24c16777c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/engine/zipball/e512298a2079acb824c9cad3984344f24c16777c", + "reference": "e512298a2079acb824c9cad3984344f24c16777c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "hyperf/guzzle": "^2.2", + "hyperf/http-message": " ^2.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^9.4", + "swoole/ide-helper": "dev-master" + }, + "suggest": { + "ext-swoole": ">=4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + }, + "hyperf": { + "config": "Hyperf\\Engine\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Engine\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "keywords": [ + "hyperf", + "php" + ], + "support": { + "issues": "https://github.com/hyperf/engine/issues", + "source": "https://github.com/hyperf/engine/tree/v1.4.1" + }, + "time": "2023-01-09T07:40:28+00:00" + }, + { + "name": "hyperf/event", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/event.git", + "reference": "e10cd5e8b7f02bad9c542c3592b495debba9a39a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/event/zipball/e10cd5e8b7f02bad9c542c3592b495debba9a39a", + "reference": "e10cd5e8b7f02bad9c542c3592b495debba9a39a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "php": ">=8.0", + "psr/event-dispatcher": "^1.0" + }, + "suggest": { + "hyperf/di": "Required to use annotatioins." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Event\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "an event manager that implements PSR-14.", + "homepage": "https://hyperf.io", + "keywords": [ + "event", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-10-13T02:40:13+00:00" + }, + { + "name": "hyperf/exception-handler", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/exception-handler.git", + "reference": "2c7506e82f81e8c60a8e83168b5af4b9eed05efd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/exception-handler/zipball/2c7506e82f81e8c60a8e83168b5af4b9eed05efd", + "reference": "2c7506e82f81e8c60a8e83168b5af4b9eed05efd", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/dispatcher": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\ExceptionHandler\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\ExceptionHandler\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Exception handler for hyperf", + "homepage": "https://hyperf.io", + "keywords": [ + "exception-handler", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/filesystem", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/filesystem.git", + "reference": "07e0cf2eaf63c000aa503ae9ed906b54ff7dd056" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/filesystem/zipball/07e0cf2eaf63c000aa503ae9ed906b54ff7dd056", + "reference": "07e0cf2eaf63c000aa503ae9ed906b54ff7dd056", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/di": "~3.0.0", + "league/flysystem": "^1.0|^2.0|^3.0", + "php": ">=8.0" + }, + "suggest": { + "hyperf/flysystem-oss": "Required to use aliyun oss adapter when use `league/flysystem` v2.0", + "hyperf/guzzle": "required to use s3 adapter", + "league/flysystem-aws-s3-v3": "required to use s3 adapter", + "league/flysystem-memory": "required to use memory adapter", + "overtrue/flysystem-cos": "Required to use cos adapter (^3.0|^4.0)", + "overtrue/flysystem-qiniu": "Required to use qiniu adapter (^1.0|^2.0)", + "xxtime/flysystem-aliyun-oss": "Required to use aliyun oss adapter when use `league/flysystem` v1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Filesystem\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Filesystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "flysystem integration for hyperf", + "keywords": [ + "hyperf", + "php" + ], + "support": { + "issues": "https://github.com/hyperf/filesystem/issues", + "source": "https://github.com/hyperf/filesystem/tree/v3.0.0" + }, + "time": "2022-08-15T15:13:16+00:00" + }, + { + "name": "hyperf/framework", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/framework.git", + "reference": "20e0fd40e200285cb5da73c9c8da6669efd7c789" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/framework/zipball/20e0fd40e200285cb5da73c9c8da6669efd7c789", + "reference": "20e0fd40e200285cb5da73c9c8da6669efd7c789", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "fig/http-message-util": "^1.1.2", + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.0|^2.0|^3.0" + }, + "suggest": { + "ext-swoole": "Required to use swoole engine.", + "hyperf/command": "Required to use Command annotation.", + "hyperf/di": "Required to use Command annotation.", + "hyperf/dispatcher": "Required to use BootApplication event.", + "symfony/event-dispatcher": "Required to use symfony event dispatcher (^5.0|^6.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Framework\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Framework\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.", + "homepage": "https://hyperf.io", + "keywords": [ + "Microservice", + "framework", + "hyperf", + "middleware", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/guzzle", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/guzzle.git", + "reference": "1df78150253171695324216a34558a182095dd0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/guzzle/zipball/1df78150253171695324216a34558a182095dd0c", + "reference": "1df78150253171695324216a34558a182095dd0c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "guzzlehttp/guzzle": "^6.3|^7.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/http-message": "^1.0" + }, + "suggest": { + "hyperf/pool": "Required to use pool handler." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Guzzle\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Guzzle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Swoole coroutine handler for guzzle", + "keywords": [ + "Guzzle", + "handler", + "php", + "swoole" + ], + "support": { + "issues": "https://github.com/hyperf/guzzle/issues", + "source": "https://github.com/hyperf/guzzle/tree/v3.0.0" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/http-message", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/hyperf/http-message.git", + "reference": "2f6f4e5079cb445d3c7c6518678cefcd726eaa52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/http-message/zipball/2f6f4e5079cb445d3c7c6518678cefcd726eaa52", + "reference": "2f6f4e5079cb445d3c7c6518678cefcd726eaa52", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/utils": "~3.0.0", + "laminas/laminas-mime": "^2.7", + "php": ">=8.0", + "psr/http-message": "^1.0" + }, + "suggest": { + "psr/container": "Required to replace RequestParserInterface." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\HttpMessage\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\HttpMessage\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "microservice framework base on swoole", + "keywords": [ + "http-message", + "hyperf", + "php", + "swoole" + ], + "support": { + "issues": "https://github.com/hyperf/http-message/issues", + "source": "https://github.com/hyperf/http-message/tree/v3.0.1" + }, + "time": "2023-01-04T11:57:10+00:00" + }, + { + "name": "hyperf/http-server", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/hyperf/http-server.git", + "reference": "192bed0596d36cb04c38695067e8c60078006109" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/http-server/zipball/192bed0596d36cb04c38695067e8c60078006109", + "reference": "192bed0596d36cb04c38695067e8c60078006109", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/dispatcher": "~3.0.0", + "hyperf/event": "~3.0.0", + "hyperf/exception-handler": "~3.0.0", + "hyperf/http-message": "~3.0.0", + "hyperf/macroable": "~3.0.0", + "hyperf/server": "~3.0.0", + "hyperf/utils": "~3.0.0", + "nikic/fast-route": "^1.3", + "php": ">=8.0", + "psr/container": "^1.0|^2.0" + }, + "suggest": { + "hyperf/di": "Required to use annotations." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\HttpServer\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\HttpServer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A HTTP Server for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "http", + "http-server", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-04T11:57:10+00:00" + }, + { + "name": "hyperf/logger", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/logger.git", + "reference": "c8de4bc7cd9ccca7c27faf3ebe4e5cfd7380c894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/logger/zipball/c8de4bc7cd9ccca7c27faf3ebe4e5cfd7380c894", + "reference": "c8de4bc7cd9ccca7c27faf3ebe4e5cfd7380c894", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "monolog/monolog": "^2.7|^3.1", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/log": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Logger\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Logger\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A logger component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "logger", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-12-11T04:49:47+00:00" + }, + { + "name": "hyperf/macroable", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/macroable.git", + "reference": "822de69ba0c75aa9767a9cea487b84bf31d5240a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/macroable/zipball/822de69ba0c75aa9767a9cea487b84bf31d5240a", + "reference": "822de69ba0c75aa9767a9cea487b84bf31d5240a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Macroable\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Hyperf Macroable package which come from illuminate/macroable", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "macroable", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/memory", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/memory.git", + "reference": "c7217c9b5177562329074d332f9e13b40693bdfe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/memory/zipball/c7217c9b5177562329074d332f9e13b40693bdfe", + "reference": "c7217c9b5177562329074d332f9e13b40693bdfe", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Memory\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Memory\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An independent component that use to operate and manage memory.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "memory", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/model-cache", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/hyperf/model-cache.git", + "reference": "9a8348253863344fb06bc51c8a4933b019c9eb5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/model-cache/zipball/9a8348253863344fb06bc51c8a4933b019c9eb5f", + "reference": "9a8348253863344fb06bc51c8a4933b019c9eb5f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/db-connection": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "suggest": { + "hyperf/event": "Required to use DeleteCacheListener." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\ModelCache\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\ModelCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A model cache component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "model-cache", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-11T09:18:46+00:00" + }, + { + "name": "hyperf/model-listener", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/model-listener.git", + "reference": "50c7bffdbab5eb3828d6314b3d1de427c0e80704" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/model-listener/zipball/50c7bffdbab5eb3828d6314b3d1de427c0e80704", + "reference": "50c7bffdbab5eb3828d6314b3d1de427c0e80704", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/database": "~3.0.0", + "hyperf/di": "~3.0.0", + "hyperf/event": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\ModelListener\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\ModelListener\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A model listener for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "model-listener", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-05-30T02:16:40+00:00" + }, + { + "name": "hyperf/paginator", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/paginator.git", + "reference": "45acff6623c65769949665d61e7af145082aea09" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/paginator/zipball/45acff6623c65769949665d61e7af145082aea09", + "reference": "45acff6623c65769949665d61e7af145082aea09", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=7.2" + }, + "suggest": { + "hyperf/event": "Reqiured to use PageResolverListener.", + "hyperf/framework": "Reqiured to use PageResolverListener.", + "hyperf/http-server": "Reqiured to use PageResolverListener." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Paginator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Paginator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A paginator component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "paginator", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/pool", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/pool.git", + "reference": "6a937ff363b84208c26cf918490a07d38ab0c987" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/pool/zipball/6a937ff363b84208c26cf918490a07d38ab0c987", + "reference": "6a937ff363b84208c26cf918490a07d38ab0c987", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Pool\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Pool\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An independent universal connection pool component.", + "homepage": "https://hyperf.io", + "keywords": [ + "connection-pool", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-26T11:47:54+00:00" + }, + { + "name": "hyperf/process", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/process.git", + "reference": "a8f7589cdde9037b313626c4d34012c8ee5be263" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/process/zipball/a8f7589cdde9037b313626c4d34012c8ee5be263", + "reference": "a8f7589cdde9037b313626c4d34012c8ee5be263", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0" + }, + "suggest": { + "hyperf/di": "Required to use annotations.", + "hyperf/event": "Required to dump the message before and after process.", + "hyperf/framework": "Required to use BootProcessListener." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Process\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Process\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A process component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "php", + "process" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/redis", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/hyperf/redis.git", + "reference": "b5e59c2612bf3f6b6fd5d024e25c973a72f62fbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/redis/zipball/b5e59c2612bf3f6b6fd5d024e25c973a72f62fbe", + "reference": "b5e59c2612bf3f6b6fd5d024e25c973a72f62fbe", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-redis": "*", + "hyperf/contract": "~3.0.0", + "hyperf/pool": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0" + }, + "suggest": { + "ext-redis": "Required to use sentinel mode (>=5.2).", + "hyperf/di": "Create the RedisPool via dependency injection." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Redis\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Redis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A redis component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "php", + "pool", + "redis" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-09T08:49:03+00:00" + }, + { + "name": "hyperf/server", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/server.git", + "reference": "467d400bb7bf7329f082887e865cb78b054ef02b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/server/zipball/467d400bb7bf7329f082887e865cb78b054ef02b", + "reference": "467d400bb7bf7329f082887e865cb78b054ef02b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.0|^2.0|^3.0", + "symfony/console": "^5.0|^6.0" + }, + "suggest": { + "hyperf/event": "Dump the info after server start.", + "hyperf/framework": "Dump the info after server start." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Server\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A base server library for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "php", + "server", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-08-15T15:13:16+00:00" + }, + { + "name": "hyperf/snowflake", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/snowflake.git", + "reference": "03c21f4be91dead4d9c0bc1a74000ce13986191b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/snowflake/zipball/03c21f4be91dead4d9c0bc1a74000ce13986191b", + "reference": "03c21f4be91dead4d9c0bc1a74000ce13986191b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "php": ">=8.0" + }, + "suggest": { + "hyperf/config": "Required to read snowflake config.", + "hyperf/redis": "Required to use RedisMilliSecondMetaGenerator or RedisSecondMetaGenerator.", + "psr/container": "Required to use MetaGeneratorFactory." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Snowflake\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Snowflake\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A snowflake library", + "homepage": "https://hyperf.io", + "keywords": [ + "php", + "snowflake" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/translation", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/translation.git", + "reference": "72c264dc92750732c456b6b9bc057d25440e4393" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/translation/zipball/72c264dc92750732c456b6b9bc057d25440e4393", + "reference": "72c264dc92750732c456b6b9bc057d25440e4393", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/macroable": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Translation\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Hyperf\\Translation\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An independent translation component, forked by illuminate/translation.", + "keywords": [ + "hyperf", + "translation" + ], + "support": { + "issues": "https://github.com/hyperf/translation/issues", + "source": "https://github.com/hyperf/translation/tree/v3.0.0" + }, + "time": "2022-11-01T02:50:54+00:00" + }, + { + "name": "hyperf/utils", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/hyperf/utils.git", + "reference": "3955bd117bb04579f1fe5644682a4e4c207521d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/utils/zipball/3955bd117bb04579f1fe5644682a4e4c207521d4", + "reference": "3955bd117bb04579f1fe5644682a4e4c207521d4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/inflector": "^2.0", + "hyperf/context": "~3.0.0", + "hyperf/contract": "~3.0.0", + "hyperf/coordinator": "~3.0.0", + "hyperf/engine": "^1.2|^2.0", + "hyperf/macroable": "~3.0.0", + "php": ">=7.2" + }, + "suggest": { + "ext-swoole": "Required to use methods related to swoole (>=4.5).", + "hyperf/di": "Required to use ExceptionNormalizer", + "nikic/php-parser": "Required to use PhpParser. (^4.0)", + "symfony/property-access": "Required to use SymfonyNormalizer (^5.0|^6.0)", + "symfony/serializer": "Required to use SymfonyNormalizer (^5.0|^6.0)", + "symfony/var-dumper": "Required to use the dd function (^5.0|^6.0)." + }, + "type": "library", + "extra": { + "hyperf": { + "config": "Hyperf\\Utils\\ConfigProvider" + }, + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Hyperf\\Utils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A tools package that could help developer solved the problem quickly.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "php", + "swoole", + "utils" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-01-04T11:57:10+00:00" + }, + { + "name": "hyperf/validation", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/validation.git", + "reference": "082ad0f0b2fc55d909a88e50cde9e56f0424e2ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/validation/zipball/082ad0f0b2fc55d909a88e50cde9e56f0424e2ce", + "reference": "082ad0f0b2fc55d909a88e50cde9e56f0424e2ce", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "egulias/email-validator": "^3.0", + "hyperf/contract": "~3.0.0", + "hyperf/database": "~3.0.0", + "hyperf/di": "~3.0.0", + "hyperf/framework": "~3.0.0", + "hyperf/http-server": "~3.0.0", + "hyperf/macroable": "~3.0.0", + "hyperf/translation": "~3.0.0", + "hyperf/utils": "~3.0.0", + "nesbot/carbon": "^2.21", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Validation\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Validation\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "hyperf validation", + "keywords": [ + "hyperf", + "validation" + ], + "support": { + "issues": "https://github.com/hyperf/validation/issues", + "source": "https://github.com/hyperf/validation/tree/v3.0.0" + }, + "time": "2022-12-22T08:23:24+00:00" + }, + { + "name": "hyperf/websocket-server", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/websocket-server.git", + "reference": "e48b49751a4bc2a0202bcaf34e6b492d57e15c79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/websocket-server/zipball/e48b49751a4bc2a0202bcaf34e6b492d57e15c79", + "reference": "e48b49751a4bc2a0202bcaf34e6b492d57e15c79", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/exception-handler": "~3.0.0", + "hyperf/http-server": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\WebSocketServer\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\WebSocketServer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A websocket server library for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "php", + "swoole", + "websocket" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-11-28T08:56:48+00:00" + }, + { + "name": "laminas/laminas-mime", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-mime.git", + "reference": "62a899a7c9100889c2d2386b1357003a2cb52fa9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/62a899a7c9100889c2d2386b1357003a2cb52fa9", + "reference": "62a899a7c9100889c2d2386b1357003a2cb52fa9", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "laminas/laminas-stdlib": "^2.7 || ^3.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0" + }, + "conflict": { + "zendframework/zend-mime": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.2.1", + "laminas/laminas-mail": "^2.12", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "laminas/laminas-mail": "Laminas\\Mail component" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Mime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Create and parse MIME messages and parts", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "mime" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-mime/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-mime/issues", + "rss": "https://github.com/laminas/laminas-mime/releases.atom", + "source": "https://github.com/laminas/laminas-mime" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2022-08-30T09:38:41+00:00" + }, + { + "name": "laminas/laminas-stdlib", + "version": "3.16.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "f4f773641807c7ccee59b758bfe4ac4ba33ecb17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/f4f773641807c7ccee59b758bfe4ac4ba33ecb17", + "reference": "f4f773641807c7ccee59b758bfe4ac4ba33ecb17", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "conflict": { + "zendframework/zend-stdlib": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^2.4.0", + "phpbench/phpbench": "^1.2.7", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.18.0", + "vimeo/psalm": "^5.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "stdlib" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-stdlib/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-stdlib/issues", + "rss": "https://github.com/laminas/laminas-stdlib/releases.atom", + "source": "https://github.com/laminas/laminas-stdlib" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2022-12-03T18:48:01+00:00" + }, + { + "name": "lcobucci/clock", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "c7aadcd6fd97ed9e199114269c0be3f335e38876" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/c7aadcd6fd97ed9e199114269c0be3f335e38876", + "reference": "c7aadcd6fd97ed9e199114269c0be3f335e38876", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "~8.1.0 || ~8.2.0", + "stella-maris/clock": "^0.1.7" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.26", + "lcobucci/coding-standard": "^9.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.27" + }, + "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.3.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2022-12-19T14:38:11+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "4.1.5", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/fe2d89f2eaa7087af4aa166c6f480ef04e000582", + "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582", + "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", + "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.5" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2021-09-28T19:34:56+00:00" + }, + { + "name": "league/flysystem", + "version": "1.1.10", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1", + "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1", + "shasum": "", + "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-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/1.1.10" + }, + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2022-10-04T09:16:37+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "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": "^3.2", + "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.11.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-04-17T13:12:02+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3", + "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-mbstring": "*", + "myclabs/php-enum": "^1.5", + "php": "^8.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.9", + "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.4", + "phpunit/phpunit": "^8.5.8 || ^9.4.2", + "vimeo/psalm": "^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + }, + { + "url": "https://opencollective.com/zipstream", + "type": "open_collective" + } + ], + "time": "2022-12-08T12:29:14+00:00" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/305444bc6fb6c89e490f4b34fa6e979584d7fa81", + "reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^9.5.16", + "predis/predis": "^1.1", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2022-07-24T12:00:55+00:00" + }, + { + "name": "myclabs/php-enum", + "version": "1.6.6", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "32c4202886c51fbe5cc3a7c34ec5c9a4a790345e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/32c4202886c51fbe5cc3a7c34ec5c9a4a790345e", + "reference": "32c4202886c51fbe5cc3a7c34ec5c9a4a790345e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ], + "support": { + "issues": "https://github.com/myclabs/php-enum/issues", + "source": "https://github.com/myclabs/php-enum/tree/master" + }, + "time": "2019-02-04T21:18:49+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.66.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "496712849902241f04902033b0441b269effe001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", + "reference": "496712849902241f04902033b0441b269effe001", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2023-01-29T18:53:47+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + }, + "time": "2020-12-01T19:48:11+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" + }, + "time": "2022-11-12T15:38:23+00:00" + }, + { + "name": "overtrue/flysystem-cos", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/overtrue/flysystem-cos.git", + "reference": "efa7578f0a497bfa53bb065ba7d4b66a8cf810f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/flysystem-cos/zipball/efa7578f0a497bfa53bb065ba7d4b66a8cf810f0", + "reference": "efa7578f0a497bfa53bb065ba7d4b66a8cf810f0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "league/flysystem": "^1.0", + "overtrue/qcloud-cos-client": "^1.0.0", + "php": ">=7.4" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.7", + "friendsofphp/php-cs-fixer": "^2.15", + "mockery/mockery": "~1.0", + "phpunit/phpunit": "~9" + }, + "type": "library", + "extra": { + "hooks": { + "pre-commit": [ + "composer test", + "composer check-style" + ], + "pre-push": [ + "composer test", + "composer check-style" + ] + } + }, + "autoload": { + "psr-4": { + "Overtrue\\Flysystem\\Cos\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "i@overtrue.me" + } + ], + "description": "Flysystem adapter for the QCloud COS storage.", + "support": { + "issues": "https://github.com/overtrue/flysystem-cos/issues", + "source": "https://github.com/overtrue/flysystem-cos/tree/3.0.2" + }, + "funding": [ + { + "url": "https://www.easywechat.com/img/pay/wechat.jpg", + "type": "custom" + }, + { + "url": "https://github.com/", + "type": "github" + }, + { + "url": "https://www.patreon.com/overtrue", + "type": "patreon" + } + ], + "time": "2021-03-04T11:44:05+00:00" + }, + { + "name": "overtrue/flysystem-qiniu", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/overtrue/flysystem-qiniu.git", + "reference": "d6edad78fb84662df4ccd41589218b373e648823" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/flysystem-qiniu/zipball/d6edad78fb84662df4ccd41589218b373e648823", + "reference": "d6edad78fb84662df4ccd41589218b373e648823", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "league/flysystem": "^1.0", + "php": ">=5.5.9", + "qiniu/php-sdk": "^7.2" + }, + "require-dev": { + "mockery/mockery": "1.3.1", + "php": ">=5.6.0", + "phpunit/phpunit": "~8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Overtrue\\Flysystem\\Qiniu\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "i@overtrue.me" + } + ], + "description": "Flysystem adapter for the Qiniu storage.", + "support": { + "issues": "https://github.com/overtrue/flysystem-qiniu/issues", + "source": "https://github.com/overtrue/flysystem-qiniu/tree/1.0.7" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2022-08-12T03:21:38+00:00" + }, + { + "name": "overtrue/qcloud-cos-client", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/overtrue/qcloud-cos-client.git", + "reference": "64ca47881afe4fd0961958f85674dcae71421913" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/qcloud-cos-client/zipball/64ca47881afe4fd0961958f85674dcae71421913", + "reference": "64ca47881afe4fd0961958f85674dcae71421913", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^7.2", + "php": ">=7.4", + "psr/http-message": "^1.0" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.7", + "friendsofphp/php-cs-fixer": "^3.4.0", + "mockery/mockery": "^1.0", + "monolog/monolog": "^2.1", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "hooks": { + "pre-commit": [ + "composer test", + "composer check-style" + ], + "pre-push": [ + "composer test", + "composer check-style" + ] + } + }, + "autoload": { + "psr-4": { + "Overtrue\\CosClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "anzhengchao@gmail.com" + } + ], + "description": "Client of QCloud.com COS", + "support": { + "issues": "https://github.com/overtrue/qcloud-cos-client/issues", + "source": "https://github.com/overtrue/qcloud-cos-client/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2022-11-07T00:18:25+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "php-amqplib/php-amqplib", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "5c537cb724f2e181183c202e63f4303935344c5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/5c537cb724f2e181183c202e63f4303935344c5f", + "reference": "5c537cb724f2e181183c202e63f4303935344c5f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-mbstring": "*", + "ext-sockets": "*", + "php": "^7.1||^8.0", + "phpseclib/phpseclib": "^2.0|^3.0" + }, + "conflict": { + "php": "7.4.0 - 7.4.1" + }, + "replace": { + "videlalvaro/php-amqplib": "self.version" + }, + "require-dev": { + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^7.5|^9.5", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpAmqpLib\\": "PhpAmqpLib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" + }, + { + "name": "Ramūnas Dronga", + "email": "github@ramuno.lt", + "role": "Maintainer" + } + ], + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", + "keywords": [ + "message", + "queue", + "rabbitmq" + ], + "support": { + "issues": "https://github.com/php-amqplib/php-amqplib/issues", + "source": "https://github.com/php-amqplib/php-amqplib/tree/v3.4.0" + }, + "time": "2022-10-18T20:52:02+00:00" + }, + { + "name": "php-di/phpdoc-reader", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PhpDocReader.git", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/66daff34cbd2627740ffec9469ffbac9f8c8185c", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpDocReader\\": "src/PhpDocReader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)", + "keywords": [ + "phpdoc", + "reflection" + ], + "support": { + "issues": "https://github.com/PHP-DI/PhpDocReader/issues", + "source": "https://github.com/PHP-DI/PhpDocReader/tree/2.2.1" + }, + "time": "2020-10-12T12:39:22+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "77a32518733312af16a44300404e945338981de3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, + "time": "2022-03-15T21:29:03+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.7.1", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "49cd7ea3d2563f028d7811f06864a53b1f15ff55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/49cd7ea3d2563f028d7811f06864a53b1f15ff55", + "reference": "49cd7ea3d2563f028d7811f06864a53b1f15ff55", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.7.1" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2022-12-08T13:30:06+00:00" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "1.27.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "eeb8582f9cabf5a7f4ef78015691163233a1834f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/eeb8582f9cabf5a7f4ef78015691163233a1834f", + "reference": "eeb8582f9cabf5a7f4ef78015691163233a1834f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "ezyang/htmlpurifier": "^4.15", + "maennchen/zipstream-php": "^2.1", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^7.4 || ^8.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "dompdf/dompdf": "^1.0 || ^2.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.2.4", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^8.5 || ^9.0", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.27.0" + }, + "time": "2023-01-24T20:07:45+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.18", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "f28693d38ba21bb0d9f0c411ee5dae2b178201da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f28693d38ba21bb0d9f0c411ee5dae2b178201da", + "reference": "f28693d38ba21bb0d9f0c411ee5dae2b178201da", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.18" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2022-12-17T18:26:50+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.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": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "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\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/aff2f80e33b7f026ec96bb42f63242dc50ffcae7", + "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-handler/issues", + "source": "https://github.com/php-fig/http-server-handler/tree/master" + }, + "time": "2018-10-30T16:46:14+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/2296f45510945530b9dceb8bcedb5cb84d40c5f5", + "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/master" + }, + "time": "2018-10-30T17:12:04+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://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/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "qiniu/php-sdk", + "version": "v7.7.0", + "source": { + "type": "git", + "url": "https://github.com/qiniu/php-sdk.git", + "reference": "dde03fc55de64815412f8ccfe24e1bd21564a6f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/dde03fc55de64815412f8ccfe24e1bd21564a6f1", + "reference": "dde03fc55de64815412f8ccfe24e1bd21564a6f1", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "myclabs/php-enum": "1.6.6", + "php": ">=5.3.3" + }, + "require-dev": { + "paragonie/random_compat": ">=2", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~3.6" + }, + "type": "library", + "autoload": { + "files": [ + "src/Qiniu/functions.php" + ], + "psr-4": { + "Qiniu\\": "src/Qiniu" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Qiniu", + "email": "sdk@qiniu.com", + "homepage": "http://www.qiniu.com" + } + ], + "description": "Qiniu Resource (Cloud) Storage SDK for PHP", + "homepage": "http://developer.qiniu.com/", + "keywords": [ + "cloud", + "qiniu", + "sdk", + "storage" + ], + "support": { + "issues": "https://github.com/qiniu/php-sdk/issues", + "source": "https://github.com/qiniu/php-sdk/tree/v7.7.0" + }, + "time": "2022-09-02T10:53:05+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "stella-maris/clock", + "version": "0.1.7", + "source": { + "type": "git", + "url": "https://github.com/stella-maris-solutions/clock.git", + "reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stella-maris-solutions/clock/zipball/fa23ce16019289a18bb3446fdecd45befcdd94f8", + "reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.0|^8.0", + "psr/clock": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "StellaMaris\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Heigl", + "role": "Maintainer" + } + ], + "description": "A pre-release of the proposed PSR-20 Clock-Interface", + "homepage": "https://gitlab.com/stella-maris/clock", + "keywords": [ + "clock", + "datetime", + "point in time", + "psr20" + ], + "support": { + "source": "https://github.com/stella-maris-solutions/clock/tree/0.1.7" + }, + "time": "2022-11-25T16:15:06+00:00" + }, + { + "name": "symfony/console", + "version": "v6.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "0f579613e771dba2dbb8211c382342a641f5da06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/0f579613e771dba2dbb8211c382342a641f5da06", + "reference": "0f579613e771dba2dbb8211c382342a641f5da06", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-12-28T14:26:22+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-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/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "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/v6.2.5" + }, + "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": "2023-01-20T17:45:48+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "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.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", + "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "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.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "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.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "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.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "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.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "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.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/property-access", + "version": "v6.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "9c267d87dd665d5d33e897290bbb9f64ae905b94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/9c267d87dd665d5d33e897290bbb9f64ae905b94", + "reference": "9c267d87dd665d5d33e897290bbb9f64ae905b94", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/property-info": "^5.4|^6.0" + }, + "require-dev": { + "symfony/cache": "^5.4|^6.0" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides functions to read and write from/to an object or array using a simple string notation", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "support": { + "source": "https://github.com/symfony/property-access/tree/v6.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-12-23T08:18:26+00:00" + }, + { + "name": "symfony/property-info", + "version": "v6.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-info.git", + "reference": "b4cbbbcc8679460cfeb1d5bdcb8127a2e85c376c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-info/zipball/b4cbbbcc8679460cfeb1d5bdcb8127a2e85c376c", + "reference": "b4cbbbcc8679460cfeb1d5bdcb8127a2e85c376c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<5.2", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/dependency-injection": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.10.4|^2", + "phpdocumentor/reflection-docblock": "^5.2", + "phpstan/phpdoc-parser": "^1.0", + "symfony/cache": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "suggest": { + "phpdocumentor/reflection-docblock": "To use the PHPDoc", + "psr/cache-implementation": "To cache results", + "symfony/doctrine-bridge": "To use Doctrine metadata", + "symfony/serializer": "To use Serializer metadata" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts information about PHP class' properties using metadata of popular sources", + "homepage": "https://symfony.com", + "keywords": [ + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" + ], + "support": { + "source": "https://github.com/symfony/property-info/tree/v6.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-12-20T16:41:15+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/string", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "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 an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.2.5" + }, + "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": "2023-01-01T08:38:09+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/60556925a703cfbc1581cde3b3f35b0bb0ea904c", + "reference": "60556925a703cfbc1581cde3b3f35b0bb0ea904c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.3|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "suggest": { + "nikic/php-parser": "To use PhpAstExtractor", + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.2.5" + }, + "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": "2023-01-05T07:00:27+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "68cce71402305a015f8c1589bfada1280dc64fe7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/68cce71402305a015f8c1589bfada1280dc64fe7", + "reference": "68cce71402305a015f8c1589bfada1280dc64fe7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T10:21:52+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19", + "reference": "2bbfbdacc8a15574f8440c4838ce0d7bb6c86b19", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.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/v6.2.5" + }, + "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": "2023-01-10T18:53:53+00:00" + }, + { + "name": "tangwei/apidocs", + "version": "v2.1.5", + "source": { + "type": "git", + "url": "https://github.com/tw2066/api-docs.git", + "reference": "58e9195b977b59d6253890cc29a313fec6ee6392" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tw2066/api-docs/zipball/58e9195b977b59d6253890cc29a313fec6ee6392", + "reference": "58e9195b977b59d6253890cc29a313fec6ee6392", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.0", + "tangwei/dto": "~2.1.0", + "tangwei/swagger-ui": "^4.6", + "zircote/swagger-php": "^4.5" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "mockery/mockery": "^1.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": ">=7.0", + "symfony/var-dumper": "^5.1" + }, + "type": "library", + "extra": { + "hyperf": { + "config": "Hyperf\\ApiDocs\\ConfigProvider" + }, + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\ApiDocs\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "TangWei", + "email": "tw2066@163.com", + "homepage": "https://github.com/tw2066", + "role": "Developer" + } + ], + "description": "A swagger library for Hyperf.", + "keywords": [ + "docs", + "hyperf", + "hyperf swagger", + "php", + "swagger" + ], + "support": { + "issues": "https://github.com/tw2066/api-docs/issues", + "source": "https://github.com/tw2066/api-docs/tree/v2.1.5" + }, + "time": "2023-01-06T14:06:22+00:00" + }, + { + "name": "tangwei/dto", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/tw2066/dto.git", + "reference": "d154741a01637f5e5509122f34cbe7c2d55eea4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tw2066/dto/zipball/d154741a01637f5e5509122f34cbe7c2d55eea4e", + "reference": "d154741a01637f5e5509122f34cbe7c2d55eea4e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/di": "~2.2.0|~3.0.0", + "hyperf/http-server": "~2.2.0|~3.0.0", + "hyperf/validation": "~2.2.0|~3.0.0", + "netresearch/jsonmapper": "~4.0.0", + "php": ">=8.0", + "phpdocumentor/reflection-docblock": "^5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "mockery/mockery": "^1.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": ">=7.0", + "symfony/var-dumper": "^5.1" + }, + "type": "library", + "extra": { + "hyperf": { + "config": "Hyperf\\DTO\\ConfigProvider" + }, + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\DTO\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "TangWei", + "email": "tw2066@163.com", + "homepage": "https://github.com/tw2066", + "role": "Developer" + } + ], + "description": "php hyperf dto", + "keywords": [ + "dto", + "hyperf", + "hyperf dto", + "hyperf swagger" + ], + "support": { + "issues": "https://github.com/tw2066/dto/issues", + "source": "https://github.com/tw2066/dto/tree/v2.1.2" + }, + "time": "2023-01-14T04:52:24+00:00" + }, + { + "name": "tangwei/swagger-ui", + "version": "v4.12.0", + "source": { + "type": "git", + "url": "https://github.com/tw2066/swagger-ui.git", + "reference": "4efed86966b5324238cb2eddcc8d43c47597182c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tw2066/swagger-ui/zipball/4efed86966b5324238cb2eddcc8d43c47597182c", + "reference": "4efed86966b5324238cb2eddcc8d43c47597182c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "tang", + "email": "tw2066@163.com" + } + ], + "description": " Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.", + "homepage": "http://swagger.io", + "keywords": [ + "Swagger dist", + "api", + "documentation", + "openapi", + "specification", + "swagger", + "ui" + ], + "support": { + "issues": "https://github.com/tw2066/swagger-ui/issues", + "source": "https://github.com/tw2066/swagger-ui/tree/v4.12.0" + }, + "time": "2022-06-20T09:03:54+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-12-12T23:22:04+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "xmo/jwt-auth", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://gitee.com/xmo/jwt-auth", + "reference": "523f2c8b4672495918241bbb76ca716deb12274c" + }, + "require": { + "ext-swoole": ">=4.6", + "lcobucci/jwt": "~4.1.0", + "nesbot/carbon": "^1.0 || ^2.0", + "php": ">=7.4" + }, + "suggest": { + "hyperf/cache": "required hyperf/cache ~2.2.0", + "hyperf/command": "required hyperf/command ~2.2.0", + "hyperf/config": "required hyperf/config ~2.2.0", + "hyperf/di": "required hyperf/di ~2.2.0", + "hyperf/utils": "required hyperf/utils ~2.2.0" + }, + "type": "library", + "extra": { + "hyperf": { + "config": "Xmo\\JWTAuth\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Xmo\\JWTAuth\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "xmo", + "email": "root@imoi.cn", + "role": "Developer" + } + ], + "description": "jwt-auth Component", + "keywords": [ + "hyperf", + "php" + ], + "time": "2022-10-07T13:16:08+00:00" + }, + { + "name": "xxtime/flysystem-aliyun-oss", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/xxtime/flysystem-aliyun-oss.git", + "reference": "ae873b5919076157b9cfeaf39d2f56d2dbb39ee9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/xxtime/flysystem-aliyun-oss/zipball/ae873b5919076157b9cfeaf39d2f56d2dbb39ee9", + "reference": "ae873b5919076157b9cfeaf39d2f56d2dbb39ee9", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "aliyuncs/oss-sdk-php": "~2.3", + "league/flysystem": "^1.0.49", + "php": ">=5.5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Xxtime\\Flysystem\\Aliyun\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joe", + "email": "joe@xxtime.com", + "homepage": "https://github.com/xxtime", + "role": "Developer" + } + ], + "description": "AliYun OSS adapter for flysystem. aliyuncs/oss-sdk-php ~2.3", + "homepage": "https://github.com/xxtime/flysystem-aliyun-oss", + "keywords": [ + "Flysystem", + "aliyun-oss", + "flysystem-aliyun-oss" + ], + "support": { + "email": "joe@xxtime.com", + "issues": "https://github.com/xxtime/flysystem-aliyun-oss/issues", + "source": "https://github.com/xxtime/flysystem-aliyun-oss/tree/1.5.0", + "wiki": "https://github.com/xxtime" + }, + "time": "2019-11-12T07:57:34+00:00" + }, + { + "name": "yurunsoft/phpmailer-swoole", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Yurunsoft/PHPMailer-Swoole.git", + "reference": "ec09ff66f6e9eb8b94b8574545357dc52e91c0a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Yurunsoft/PHPMailer-Swoole/zipball/ec09ff66f6e9eb8b94b8574545357dc52e91c0a5", + "reference": "ec09ff66f6e9eb8b94b8574545357dc52e91c0a5", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.0", + "phpmailer/phpmailer": "~6.0" + }, + "require-dev": { + "swoft/swoole-ide-helper": "~2.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/PHPMailer/functions.php" + ], + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/PHPMailer", + "Yurun\\Util\\Swoole\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "description": "PHPMailer 支持 Swoole 协程环境", + "support": { + "issues": "https://github.com/Yurunsoft/PHPMailer-Swoole/issues", + "source": "https://github.com/Yurunsoft/PHPMailer-Swoole/tree/master" + }, + "time": "2019-10-24T07:46:26+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "4.6.0", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "ee8147745c92dd4404adb531751ce32676b888d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/ee8147745c92dd4404adb531751ce32676b888d4", + "reference": "ee8147745c92dd4404adb531751ce32676b888d4", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/annotations": "^1.7 || ^2.0", + "ext-json": "*", + "php": ">=7.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/deprecation-contracts": "^2 || ^3", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11", + "friendsofphp/php-cs-fixer": "^2.17 || ^3.0", + "phpstan/phpstan": "^1.6", + "phpunit/phpunit": ">=8", + "vimeo/psalm": "^4.23" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenApi\\": "src" + } + }, + "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/4.6.0" + }, + "time": "2023-01-13T00:51:01+00:00" + }, + { + "name": "zoujingli/ip2region", + "version": "v1.0.12", + "source": { + "type": "git", + "url": "https://github.com/zoujingli/ip2region.git", + "reference": "82cebc7a6be46524797454e98d3b165521065c26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zoujingli/ip2region/zipball/82cebc7a6be46524797454e98d3b165521065c26", + "reference": "82cebc7a6be46524797454e98d3b165521065c26", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Ip2Region.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com", + "homepage": "http://ctolog.com" + } + ], + "description": "Ip2Region for PHP", + "homepage": "https://github.com/zoujingli/Ip2Region", + "keywords": [ + "Ip2Region" + ], + "support": { + "issues": "https://github.com/zoujingli/ip2region/issues", + "source": "https://github.com/zoujingli/ip2region/tree/v1.0.12" + }, + "time": "2022-03-03T10:24:30+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/pcre", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4482b6409ca6bfc2af043a5711cd21ac3e7a8dfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4482b6409ca6bfc2af043a5711cd21ac3e7a8dfb", + "reference": "4482b6409ca6bfc2af043a5711cd21ac3e7a8dfb", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.0.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-03T20:24:16+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.14.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "14f0541651841b63640e7aafad041ad55dc7aa88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/14f0541651841b63640e7aafad041ad55dc7aa88", + "reference": "14f0541651841b63640e7aafad041ad55dc7aa88", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "composer/semver": "^3.3", + "composer/xdebug-handler": "^3.0.3", + "doctrine/annotations": "^1.14.2 || ^2", + "doctrine/lexer": "^2", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.27", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" + }, + "require-dev": { + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.0", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.5.3", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.16", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.6", + "phpunitgoodpractices/traits": "^1.9.2", + "symfony/phpunit-bridge": "^6.2.3", + "symfony/yaml": "^5.4 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.2" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2023-01-29T23:47:01+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "hyperf/devtool", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/devtool.git", + "reference": "715167d5160261c5f87ef82def1298ad6edb19ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/devtool/zipball/715167d5160261c5f87ef82def1298ad6edb19ad", + "reference": "715167d5160261c5f87ef82def1298ad6edb19ad", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/command": "~3.0.0", + "hyperf/contract": "~3.0.0", + "hyperf/di": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Devtool\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Devtool\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A Devtool for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "devtool", + "hyperf", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-12-10T13:24:37+00:00" + }, + { + "name": "hyperf/ide-helper", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/ide-helper.git", + "reference": "14d8a8ee13435a05e1ecf1d2b0e276efbcc82366" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/ide-helper/zipball/14d8a8ee13435a05e1ecf1d2b0e276efbcc82366", + "reference": "14d8a8ee13435a05e1ecf1d2b0e276efbcc82366", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "IDE help files for Hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "hyperf", + "ide-helper", + "php", + "swoole" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2022-05-30T02:16:40+00:00" + }, + { + "name": "hyperf/testing", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/hyperf/testing.git", + "reference": "b4e9ed466a192e5af72b3d456d11da1d54304c54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/testing/zipball/b4e9ed466a192e5af72b3d456d11da1d54304c54", + "reference": "b4e9ed466a192e5af72b3d456d11da1d54304c54", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hyperf/contract": "~3.0.0", + "hyperf/http-message": "~3.0.0", + "hyperf/http-server": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "phpunit/phpunit": "^9.5", + "psr/container": "^1.0|^2.0" + }, + "bin": [ + "co-phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\Testing\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Testing for hyperf", + "keywords": [ + "php", + "swoole", + "testing" + ], + "support": { + "source": "https://github.com/hyperf/testing/tree/v3.0.1" + }, + "time": "2023-01-04T11:57:10+00:00" + }, + { + "name": "hyperf/watcher", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hyperf/watcher.git", + "reference": "2fa0770171d3e1d7576f3c66a9cbeb479fe1daca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/watcher/zipball/2fa0770171d3e1d7576f3c66a9cbeb479fe1daca", + "reference": "2fa0770171d3e1d7576f3c66a9cbeb479fe1daca", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-posix": "*", + "hyperf/command": "~3.0.0", + "hyperf/di": "~3.0.0", + "hyperf/framework": "~3.0.0", + "php": ">=8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\Watcher\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Hyperf\\Watcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Hot reload watcher for Hyperf", + "keywords": [ + "hyperf", + "php" + ], + "support": { + "issues": "https://github.com/hyperf/watcher/issues", + "source": "https://github.com/hyperf/watcher/tree/v3.0.0" + }, + "time": "2022-12-27T02:58:03+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.5.1" + }, + "time": "2022-09-07T15:32:08+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "0.12.100", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "48236ddf823547081b2b153d1cd2994b784328c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/48236ddf823547081b2b153d1cd2994b784328c3", + "reference": "48236ddf823547081b2b153d1cd2994b784328c3", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.1|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.12-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/0.12.100" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-11-01T09:52:08+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.24", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.14", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-01-26T08:26:55+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.28", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-01-14T12:32:24+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-04-03T09:37:03+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-12T14:47:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "swoole/ide-helper", + "version": "4.8.12", + "source": { + "type": "git", + "url": "https://github.com/swoole/ide-helper.git", + "reference": "afe3a09f8c49a6011e2206a03e55e391d97d81b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/afe3a09f8c49a6011e2206a03e55e391d97d81b0", + "reference": "afe3a09f8c49a6011e2206a03e55e391d97d81b0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Team Swoole", + "email": "team@swoole.com" + } + ], + "description": "IDE help files for Swoole.", + "support": { + "issues": "https://github.com/swoole/ide-helper/issues", + "source": "https://github.com/swoole/ide-helper/tree/4.8.12" + }, + "funding": [ + { + "url": "https://gitee.com/swoole/swoole?donate=true", + "type": "custom" + }, + { + "url": "https://github.com/swoole", + "type": "github" + } + ], + "time": "2022-09-22T16:31:12+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", + "reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.5" + }, + "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": "2023-01-01T08:38:09+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "02ff5eea2f453731cfbc6bc215e456b781480448" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/02ff5eea2f453731cfbc6bc215e456b781480448", + "reference": "02ff5eea2f453731cfbc6bc215e456b781480448", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.1.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": "2022-02-25T11:15:52+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593", + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.2.5" + }, + "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": "2023-01-20T17:45:48+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "e8324d44f5af99ec2ccec849934a242f64458f86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/e8324d44f5af99ec2ccec849934a242f64458f86", + "reference": "e8324d44f5af99ec2ccec849934a242f64458f86", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.2.5" + }, + "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": "2023-01-01T08:38:09+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "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.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "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 backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/process", + "version": "v6.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/ba6e55359f8f755fe996c58a81e00eaa67a35877", + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-02T09:08:04+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/00b6ac156aacffc53487c930e0ab14587a6607f6", + "reference": "00b6ac156aacffc53487c930e0ab14587a6607f6", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^1|^2|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "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": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.2.5" + }, + "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": "2023-01-01T08:36:55+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.0", + "ext-gd": "*", + "ext-json": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-pdo_mysql": "*", + "ext-redis": "*", + "ext-swoole": ">=4.5" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/config/autoload/amqp.php b/config/autoload/amqp.php new file mode 100644 index 0000000..934cf4b --- /dev/null +++ b/config/autoload/amqp.php @@ -0,0 +1,40 @@ + [ + 'host' => env('AMQP_HOST', 'localhost'), + 'port' => (int) env('AMQP_PORT', 5672), + 'user' => env('AMQP_USER', 'guest'), + 'password' => env('AMQP_PASSWORD', 'guest'), + 'vhost' => env('AMQP_VHOST', '/'), + 'concurrent' => [ + 'limit' => 1, + ], + 'pool' => [ + 'connections' => 2, + ], + 'params' => [ + 'insist' => false, + 'login_method' => 'AMQPLAIN', + 'login_response' => null, + 'locale' => 'en_US', + 'connection_timeout' => 3, + 'read_write_timeout' => 6, + 'context' => null, + 'keepalive' => true, + 'heartbeat' => 3, + 'channel_rpc_timeout' => 0.0, + 'close_on_destruct' => false, + 'max_idle_channels' => 10, + ], + ], +]; diff --git a/config/autoload/annotations.php b/config/autoload/annotations.php new file mode 100644 index 0000000..a70aab4 --- /dev/null +++ b/config/autoload/annotations.php @@ -0,0 +1,25 @@ + [ + 'paths' => [ + BASE_PATH . '/app', + BASE_PATH . '/addon', + BASE_PATH . '/builder', + BASE_PATH . '/api' + ], + 'ignore_annotations' => [ + 'mixin', + 'required' + ], + ], +]; diff --git a/config/autoload/api_docs.php b/config/autoload/api_docs.php new file mode 100644 index 0000000..9654c26 --- /dev/null +++ b/config/autoload/api_docs.php @@ -0,0 +1,49 @@ + env('APP_ENV') !== 'prod', + 'format' => 'json', + 'output_dir' => BASE_PATH . '/runtime/swagger', + 'prefix_url' => env('API_DOCS_PREFIX_URL', '/swagger'), + // 替换验证属性 + 'validation_custom_attributes' => true, + // 全局responses + 'responses' => [ + ['response' => 401, 'description' => 'Unauthorized'], + ['response' => 500, 'description' => 'System error'], + ], + // swagger 的基础配置 会映射到OpenAPI对象 + 'swagger' => [ + 'info' => [ + 'title' => 'MineAdmin API DOC', + 'version' => '1.1', + 'description' => 'MineAdmin后台接口列表', + ], + 'servers' => [ + [ + 'url' => 'http://127.0.0.1:9501', + 'description' => '本地服务器', + ], + ], + 'components' => [ + 'securitySchemes' => [ + [ + 'securityScheme' => 'Authorization', + 'type' => 'apiKey', + 'in' => 'header', + 'name' => 'Authorization', + ], + ], + ], + 'security' => [ + ['Authorization' => []], + ], + 'externalDocs' => [ + 'description' => 'Find out more about Swagger', + 'url' => 'https://github.com/tw2066/api-docs', + ], + ], +]; diff --git a/config/autoload/aspects.php b/config/autoload/aspects.php new file mode 100644 index 0000000..f46bd96 --- /dev/null +++ b/config/autoload/aspects.php @@ -0,0 +1,13 @@ + [ + 'driver' => Hyperf\Cache\Driver\RedisDriver::class, + 'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class, + 'prefix' => 'Tkview:', + ], +]; diff --git a/config/autoload/commands.php b/config/autoload/commands.php new file mode 100644 index 0000000..f46bd96 --- /dev/null +++ b/config/autoload/commands.php @@ -0,0 +1,13 @@ + [ + 'driver' => env('DB_DRIVER', 'mysql'), + 'host' => env('DB_HOST', 'localhost'), + 'database' => env('DB_DATABASE', 'hyperf'), + 'port' => env('DB_PORT', 3306), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', 'root'), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => env('DB_PREFIX', ''), + 'pool' => [ + 'min_connections' => 1, + 'max_connections' => 20, + 'connect_timeout' => 10.0, + 'wait_timeout' => 3.0, + 'heartbeat' => -1, + 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60), + ], + 'cache' => [ + 'handler' => \Hyperf\ModelCache\Handler\RedisHandler::class, + 'cache_key' => 'MineAdmin:%s:m:%s:%s:%s', + 'prefix' => 'model-cache', + 'ttl' => 86400 * 7, + 'empty_model_ttl' => 60, + 'load_script' => true, + 'use_default_value' => false, + ], + 'commands' => [ + 'gen:model' => [ + 'path' => 'app/Model', + 'force_casts' => true, + 'inheritance' => 'MineModel', + 'uses' => 'Builder\MineModel', + 'with_comments' => true, + 'refresh_fillable' => true, + 'visitors' => [ + Hyperf\Database\Commands\Ast\ModelRewriteKeyInfoVisitor::class, + Hyperf\Database\Commands\Ast\ModelRewriteTimestampsVisitor::class, + Hyperf\Database\Commands\Ast\ModelRewriteSoftDeletesVisitor::class, +// Hyperf\Database\Commands\Ast\ModelRewriteGetterSetterVisitor::class, + ], + ], + ], + ], +]; diff --git a/config/autoload/dependencies.php b/config/autoload/dependencies.php new file mode 100644 index 0000000..b33acb7 --- /dev/null +++ b/config/autoload/dependencies.php @@ -0,0 +1,16 @@ + Builder\MineModelVisitor::class, + Hyperf\HttpServer\CoreMiddleware::class => Builder\Middlewares\HttpCoreMiddleware::class, + Builder\Interfaces\UserServiceInterface::class => App\System\Service\Dependencies\UserAuthService::class, +]; diff --git a/config/autoload/devtool.php b/config/autoload/devtool.php new file mode 100644 index 0000000..3c2f607 --- /dev/null +++ b/config/autoload/devtool.php @@ -0,0 +1,44 @@ + [ + 'amqp' => [ + 'consumer' => [ + 'namespace' => 'App\\Amqp\\Consumer', + ], + 'producer' => [ + 'namespace' => 'App\\Amqp\\Producer', + ], + ], + 'aspect' => [ + 'namespace' => 'App\\Aspect', + ], + 'command' => [ + 'namespace' => 'App\\Command', + ], + 'controller' => [ + 'namespace' => 'App\\Controller', + ], + 'job' => [ + 'namespace' => 'App\\Job', + ], + 'listener' => [ + 'namespace' => 'App\\Listener', + ], + 'middleware' => [ + 'namespace' => 'App\\Middleware', + ], + 'Process' => [ + 'namespace' => 'App\\Processes', + ], + ], +]; diff --git a/config/autoload/dump-server.php b/config/autoload/dump-server.php new file mode 100644 index 0000000..155442d --- /dev/null +++ b/config/autoload/dump-server.php @@ -0,0 +1,8 @@ + env('DUMP_SERVER_HOST', 'tcp://127.0.0.1:9912'), +]; \ No newline at end of file diff --git a/config/autoload/exceptions.php b/config/autoload/exceptions.php new file mode 100644 index 0000000..3c4d2d2 --- /dev/null +++ b/config/autoload/exceptions.php @@ -0,0 +1,24 @@ + [ + 'http' => [ + Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class, + Builder\Exception\Handler\ValidationExceptionHandler::class, + Builder\Exception\Handler\TokenExceptionHandler::class, + Builder\Exception\Handler\NoPermissionExceptionHandler::class, + Builder\Exception\Handler\NormalStatusExceptionHandler::class, + Builder\Exception\Handler\AppExceptionHandler::class, + ], + ], +]; diff --git a/config/autoload/file.php b/config/autoload/file.php new file mode 100644 index 0000000..81500f7 --- /dev/null +++ b/config/autoload/file.php @@ -0,0 +1,106 @@ + 'local', + 'storage' => [ + 'local' => [ + 'driver' => \Hyperf\Filesystem\Adapter\LocalAdapterFactory::class, + 'root' => __DIR__ . '/../../public/' . env('UPLOAD_PATH', 'uploads'), + ], + 'oss' => [ + 'driver' => \Hyperf\Filesystem\Adapter\AliyunOssAdapterFactory::class, + 'accessId' => '', + 'accessSecret' => '', + 'bucket' => '', + 'endpoint' => '', + 'domain' => '', + 'schema' => 'http://', + 'isCName' => false, + // 'timeout' => 3600, + // 'connectTimeout' => 10, + // 'token' => '', + ], + 'qiniu' => [ + 'driver' => \Hyperf\Filesystem\Adapter\QiniuAdapterFactory::class, + 'accessKey' => '', + 'secretKey' => '', + 'bucket' => '', + 'domain' => '', + 'schema' => 'http://', + ], + 'cos' => [ + 'driver' => \Hyperf\Filesystem\Adapter\CosAdapterFactory::class, + 'region' => '', + 'domain' => '', + 'schema' => 'http://', + // overtrue/flysystem-cos ^2.0 配置如下 + 'credentials' => [ + 'appId' => '', + 'secretId' => '', + 'secretKey' => '', + ], + // overtrue/flysystem-cos ^3.0 配置如下 + // 'app_id' => env('COS_APPID'), + // 'secret_id' => env('COS_SECRET_ID'), + // 'secret_key' => env('COS_SECRET_KEY'), + // 可选,如果 bucket 为私有访问请打开此项 + // 'signed_url' => false, + 'bucket' => '', + 'read_from_cdn' => false, + // 'timeout' => 60, + // 'connect_timeout' => 60, + // 'cdn' => '', + // 'scheme' => 'https', + ], + 'ftp' => [ + 'driver' => \Hyperf\Filesystem\Adapter\FtpAdapterFactory::class, + 'host' => 'ftp.example.com', + 'username' => 'username', + 'password' => 'password', + // 'port' => 21, + // 'root' => '/path/to/root', + // 'passive' => true, + // 'ssl' => true, + // 'timeout' => 30, + // 'ignorePassiveAddress' => false, + ], + 'memory' => [ + 'driver' => \Hyperf\Filesystem\Adapter\MemoryAdapterFactory::class, + ], + 's3' => [ + 'driver' => \Hyperf\Filesystem\Adapter\S3AdapterFactory::class, + 'credentials' => [ + 'key' => env('S3_KEY'), + 'secret' => env('S3_SECRET'), + ], + 'region' => env('S3_REGION'), + 'version' => 'latest', + 'bucket_endpoint' => false, + 'use_path_style_endpoint' => false, + 'endpoint' => env('S3_ENDPOINT'), + 'bucket_name' => env('S3_BUCKET'), + ], + 'minio' => [ + 'driver' => \Hyperf\Filesystem\Adapter\S3AdapterFactory::class, + 'credentials' => [ + 'key' => env('S3_KEY'), + 'secret' => env('S3_SECRET'), + ], + 'region' => env('S3_REGION'), + 'version' => 'latest', + 'bucket_endpoint' => false, + 'use_path_style_endpoint' => true, + 'endpoint' => env('S3_ENDPOINT'), + 'bucket_name' => env('S3_BUCKET'), + ], + ], +]; diff --git a/config/autoload/jwt.php b/config/autoload/jwt.php new file mode 100644 index 0000000..88459d5 --- /dev/null +++ b/config/autoload/jwt.php @@ -0,0 +1,115 @@ + env('JWT_LOGIN_TYPE', 'sso'), // 登录方式,sso为单点登录,mpop为多点登录 + + /** + * 单点登录自定义数据中必须存在uid的键值,这个key你可以自行定义,只要自定义数据中存在该键即可 + */ + 'sso_key' => 'id', + + 'secret' => env('JWT_SECRET', 'mineAdmin'), // 非对称加密使用字符串,请使用自己加密的字符串 + + /** + * JWT 权限keys + * 对称算法: HS256, HS384 & HS512 使用 `JWT_SECRET`. + * 非对称算法: RS256, RS384 & RS512 / ES256, ES384 & ES512 使用下面的公钥私钥. + */ + 'keys' => [ + 'public' => env('JWT_PUBLIC_KEY'), // 公钥,例如:'file:///path/to/public/key' + 'private' => env('JWT_PRIVATE_KEY'), // 私钥,例如:'file:///path/to/private/key' + ], + + 'ttl' => env('JWT_TTL', 7200), // token过期时间,单位为秒 + + 'alg' => env('JWT_ALG', 'HS256'), // jwt的hearder加密算法 + + /** + * 支持的算法 + */ + 'supported_algs' => [ + 'HS256' => 'Lcobucci\JWT\Signer\Hmac\Sha256', + 'HS384' => 'Lcobucci\JWT\Signer\Hmac\Sha384', + 'HS512' => 'Lcobucci\JWT\Signer\Hmac\Sha512', + 'ES256' => 'Lcobucci\JWT\Signer\Ecdsa\Sha256', + 'ES384' => 'Lcobucci\JWT\Signer\Ecdsa\Sha384', + 'ES512' => 'Lcobucci\JWT\Signer\Ecdsa\Sha512', + 'RS256' => 'Lcobucci\JWT\Signer\Rsa\Sha256', + 'RS384' => 'Lcobucci\JWT\Signer\Rsa\Sha384', + 'RS512' => 'Lcobucci\JWT\Signer\Rsa\Sha512', + ], + + /** + * 对称算法名称 + */ + 'symmetry_algs' => [ + 'HS256', + 'HS384', + 'HS512' + ], + + /** + * 非对称算法名称 + */ + 'asymmetric_algs' => [ + 'RS256', + 'RS384', + 'RS512', + 'ES256', + 'ES384', + 'ES512', + ], + + /** + * 是否开启黑名单,单点登录和多点登录的注销、刷新使原token失效,必须要开启黑名单,目前黑名单缓存只支持hyperf缓存驱动 + */ + 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), + + /** + * 黑名单的宽限时间 单位为:秒,注意:如果使用单点登录,该宽限时间无效 + */ + 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), + + /** + * 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为1天,最好设置跟过期时间一样 + */ + 'blacklist_cache_ttl' => env('JWT_TTL', 7200), + + 'blacklist_prefix' => 'MineAdmin_jwt', // 黑名单缓存的前缀 + + /** + * 区分不同场景的token,比如你一个项目可能会有多种类型的应用接口鉴权,下面自行定义,我只是举例子 + * 下面的配置会自动覆盖根配置,比如application1会里面的数据会覆盖掉根数据 + * 下面的scene会和根数据合并 + * scene必须存在一个default + * 什么叫根数据,这个配置的一维数组,除了scene都叫根配置 + */ + 'scene' => [ + 'default' => [], + 'api' => [ + 'secret' => env('JWT_API_SECRET', 'api_verify'), // 非对称加密使用字符串,请使用自己加密的字符串 + 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 + 'sso_key' => 'id', + 'ttl' => 7200, // token过期时间,单位为秒 + 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 + ], + 'application2' => [ + 'secret' => 'application2', // 非对称加密使用字符串,请使用自己加密的字符串 + 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 + 'sso_key' => 'uid', + 'ttl' => 7200, // token过期时间,单位为秒 + 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 + ], + 'application3' => [ + 'secret' => 'application3', // 非对称加密使用字符串,请使用自己加密的字符串 + 'login_type' => 'mppo', // 登录方式,sso为单点登录,mpop为多点登录 + 'ttl' => 7200, // token过期时间,单位为秒 + 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 + ] + ], + 'model' => [ + 'class' => 'App\System\Model\SystemUser', + 'pk' => 'id' + ] +]; diff --git a/config/autoload/listeners.php b/config/autoload/listeners.php new file mode 100644 index 0000000..f46bd96 --- /dev/null +++ b/config/autoload/listeners.php @@ -0,0 +1,13 @@ + [ + 'handler' => [ + 'class' => Monolog\Handler\RotatingFileHandler::class, + 'constructor' => [ + 'filename' => BASE_PATH . '/runtime/logs/debug/view.log', + 'level' => Monolog\Logger::DEBUG, + ], + ], + 'formatter' => [ + 'class' => Monolog\Formatter\LineFormatter::class, + 'constructor' => [ + 'format' => null, + 'dateFormat' => 'Y-m-d H:i:s', + 'allowInlineLineBreaks' => true, + ], + ], + ], + 'sql' => [ + 'handler' => [ + 'class' => Monolog\Handler\RotatingFileHandler::class, + 'constructor' => [ + 'filename' => BASE_PATH . '/runtime/logs/sql/sql.log', + 'level' => Monolog\Logger::DEBUG, + ], + ], + 'formatter' => [ + 'class' => Monolog\Formatter\LineFormatter::class, + 'constructor' => [ + 'format' => null, + 'dateFormat' => 'Y-m-d H:i:s', + 'allowInlineLineBreaks' => true, + ], + ], + ], +]; diff --git a/config/autoload/middlewares.php b/config/autoload/middlewares.php new file mode 100644 index 0000000..a56a5b1 --- /dev/null +++ b/config/autoload/middlewares.php @@ -0,0 +1,17 @@ + [ + \Hyperf\Validation\Middleware\ValidationMiddleware::class, +// \Builder\Middlewares\CheckModuleMiddleware::class //验证模块是有使用权限 + ], +]; diff --git a/config/autoload/mineadmin.php b/config/autoload/mineadmin.php new file mode 100644 index 0000000..8a6bb17 --- /dev/null +++ b/config/autoload/mineadmin.php @@ -0,0 +1,22 @@ + + * @Link https://gitee.com/xmo/MineAdmin + */ + +return [ + // 是否启用数据权限 + 'data_scope_enabled' => true, + /** + * excel 导入、导出驱动类型 auto, xlsWriter, phpOffice + * auto 优先使用xlsWriter,若环境没有安装xlsWriter扩展则使用phpOffice + */ + 'excel_drive' => 'auto' +]; \ No newline at end of file diff --git a/config/autoload/processes.php b/config/autoload/processes.php new file mode 100644 index 0000000..c0501c2 --- /dev/null +++ b/config/autoload/processes.php @@ -0,0 +1,14 @@ + [ + 'host' => env('REDIS_HOST', 'localhost'), + 'auth' => env('REDIS_AUTH', null), + 'port' => (int) env('REDIS_PORT', 6379), + 'db' => (int) env('REDIS_DB', 0), + 'pool' => [ + 'min_connections' => 1, + 'max_connections' => 10, + 'connect_timeout' => 10.0, + 'wait_timeout' => 3.0, + 'heartbeat' => -1, + 'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60), + ], + ], +]; diff --git a/config/autoload/server.php b/config/autoload/server.php new file mode 100644 index 0000000..b425ff0 --- /dev/null +++ b/config/autoload/server.php @@ -0,0 +1,71 @@ + SWOOLE_PROCESS, + 'servers' => [ + [ + 'name' => 'http', + 'type' => Server::SERVER_HTTP, + 'host' => '0.0.0.0', + 'port' => 9501, + 'sock_type' => SWOOLE_SOCK_TCP, + 'callbacks' => [ + Event::ON_REQUEST => [MineServer::class, 'onRequest'], + ], + ], + [ + 'name' => 'message', + 'type' => Server::SERVER_WEBSOCKET, + 'host' => '0.0.0.0', + 'port' => 9502, + 'sock_type' => SWOOLE_SOCK_TCP, + 'callbacks' => [ + Event::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'], + Event::ON_MESSAGE => [Hyperf\WebSocketServer\Server::class, 'onMessage'], + Event::ON_CLOSE => [Hyperf\WebSocketServer\Server::class, 'onClose'], + ], + 'settings' => [ + // 心跳检测 + 'heartbeat_idle_time' => 60, + 'heartbeat_check_interval' => 30, + ] + ], + ], + 'settings' => [ + // 对外部可以直接访问的目录地址,建议使用nginx反向代理访问 + Constant::OPTION_DOCUMENT_ROOT => BASE_PATH . '/public', + // 开启外部可以访问 + Constant::OPTION_ENABLE_STATIC_HANDLER => true, + Constant::OPTION_ENABLE_COROUTINE => true, + Constant::OPTION_WORKER_NUM => swoole_cpu_num(), + Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid', + Constant::OPTION_OPEN_TCP_NODELAY => true, + Constant::OPTION_MAX_COROUTINE => 100000, + Constant::OPTION_OPEN_HTTP2_PROTOCOL => true, + Constant::OPTION_MAX_REQUEST => 100000, + Constant::OPTION_SOCKET_BUFFER_SIZE => 3 * 1024 * 1024, + Constant::OPTION_BUFFER_OUTPUT_SIZE => 3 * 1024 * 1024, + // 上传最大为4M + Constant::OPTION_PACKAGE_MAX_LENGTH => 4 * 1024 * 1024 + ], + 'callbacks' => [ + Event::ON_BEFORE_START => [Builder\MineStart::class, 'beforeStart'], + Event::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'], + Event::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'], + Event::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'], + ], +]; diff --git a/config/autoload/snowflake.php b/config/autoload/snowflake.php new file mode 100644 index 0000000..279de42 --- /dev/null +++ b/config/autoload/snowflake.php @@ -0,0 +1,24 @@ + MetaGeneratorInterface::DEFAULT_BEGIN_SECOND, + RedisMilliSecondMetaGenerator::class => [ + 'pool' => 'default', + ], + RedisSecondMetaGenerator::class => [ + 'pool' => 'default', + ], +]; diff --git a/config/autoload/translation.php b/config/autoload/translation.php new file mode 100644 index 0000000..72d905d --- /dev/null +++ b/config/autoload/translation.php @@ -0,0 +1,16 @@ + 'zh_CN', + 'fallback_locale' => 'zh_CN', + 'path' => BASE_PATH . '/storage/languages', +]; diff --git a/config/autoload/watcher.php b/config/autoload/watcher.php new file mode 100644 index 0000000..a2aedbe --- /dev/null +++ b/config/autoload/watcher.php @@ -0,0 +1,23 @@ + ScanFileDriver::class, + 'bin' => 'php', + 'watch' => [ + 'dir' => ['api', 'app', 'config', 'mine','addon'], + 'file' => ['.env'], + 'scan_interval' => 2000, + ], +]; diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..22b65f4 --- /dev/null +++ b/config/config.php @@ -0,0 +1,31 @@ + env('APP_NAME', 'mineAdmin'), + 'app_env' => env('APP_ENV', 'dev'), + 'scan_cacheable' => env('SCAN_CACHEABLE', false), + StdoutLoggerInterface::class => [ + 'log_level' => [ + LogLevel::ALERT, + LogLevel::CRITICAL, +// LogLevel::DEBUG, + LogLevel::EMERGENCY, + LogLevel::ERROR, + LogLevel::INFO, + LogLevel::NOTICE, + LogLevel::WARNING, + ], + ], +]; diff --git a/config/container.php b/config/container.php new file mode 100644 index 0000000..2a09133 --- /dev/null +++ b/config/container.php @@ -0,0 +1,24 @@ + [ \App\System\Middleware\WsAuthMiddleware::class ] + ]); +}); diff --git a/deploy.test.yml b/deploy.test.yml new file mode 100644 index 0000000..2f98f69 --- /dev/null +++ b/deploy.test.yml @@ -0,0 +1,30 @@ +version: '3.7' +services: + hyperf: + image: $REGISTRY_URL/$PROJECT_NAME:test + environment: + - "APP_PROJECT=hyperf" + - "APP_ENV=test" + ports: + - 9501:9501 + deploy: + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 5 + update_config: + parallelism: 2 + delay: 5s + order: start-first + networks: + - hyperf_net + configs: + - source: hyperf_v1.0 + target: /opt/www/.env +configs: + hyperf_v1.0: + external: true +networks: + hyperf_net: + external: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8f413a3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +version: '3' +services: + # 首先下载前端,https://gitee.com/mineadmin/mineadmin-vue + # 在后端根目录建立mine-ui目录,把前端文件复制过来。 + # 容器内访问宿主机的地址用:host.docker.internal + # 宿主机也可以在hosts文件添加:127.0.0.1 host.docker.internal + # mine-ui的.env.development文件127.0.0.1替换为host.docker.internal + nginx-frontend: + image: nginx:latest + environment: + - "TIMEZONE=Asia/Shanghai" + ports: + - 8101:80 + volumes: + - ./mine-ui/dist:/usr/share/nginx/html + - ./docker/nginx-frontend/servers:/etc/nginx/conf.d + + # mineadmin的.env的mysql、redis如果连接到宿主机,地址用:host.docker.internal + # 如果是连接到docker内服务,则用服务名称,如:redis + hyperf: + image: ixmo/mine-admin:latest + restart: always + environment: + - "TIMEZONE=Asia/Shanghai" + - "APP_PROJECT=hyperf" + - "APP_SYSTEM_ENV=docker" + working_dir: "/opt/www" + ports: + - 9501:9501 + - 9502:9502 + - 9503:9503 + volumes: + - ./:/opt/www + entrypoint: ["php", "watch", "-c"] + + # 没有将volumes映射出来,需要持久化自行百度一下 + + mysql: + container_name: mysql + environment: + - "TZ=Asia/Shanghai" + - MYSQL_ROOT_PASSWORD=12345678 + - "explicit_defaults_for_timestamp=true" + - "lower_case_table_names=1" + image: mysql:5.7 + restart: always + # command: "--default-authentication-plugin=mysql_native_password" + redis: + image: redis + container_name: redis + restart: always \ No newline at end of file diff --git a/docker/nginx-frontend/servers/mine-frontend.conf b/docker/nginx-frontend/servers/mine-frontend.conf new file mode 100644 index 0000000..09801d9 --- /dev/null +++ b/docker/nginx-frontend/servers/mine-frontend.conf @@ -0,0 +1,38 @@ +# 至少需要一个 Hyperf 节点,多个配置多行 +upstream hyperf { + # Hyperf HTTP Server 的 IP 及 端口 + server hyperf:9501; +} + +server { + # 端口 + listen 80; + # 域名 + server_name demo.mineadmin.com; + # 日志 + error_log /var/log/nginx/error.log error; + access_log /var/log/nginx/access.log; + + # 同域根目录前端代码部署,注意: + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html; + } + + # 支持自定义下划线参数通过header传输 + # underscores_in_headers on; + + # PHP 代码 + location /api/ { + # 将客户端的 Host 和 IP 信息一并转发到对应节点 + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # 将协议架构转发到对应节点,如果使用非https请改为http + proxy_set_header X-scheme https; + + # 执行代理访问真实服务器 + proxy_pass http://hyperf/; + } +} \ No newline at end of file diff --git a/docker/nginx-frontend/servers/mine-websocket.conf b/docker/nginx-frontend/servers/mine-websocket.conf new file mode 100644 index 0000000..b7f969f --- /dev/null +++ b/docker/nginx-frontend/servers/mine-websocket.conf @@ -0,0 +1,32 @@ +# 至少需要一个 message 节点,多个配置多行 +upstream message { + # Hyperf HTTP Server 的 IP 及 端口 + server hyperf:9502; +} +server { + # 端口 + listen 80; + # 域名 + server_name message.mineadmin.com; + # 日志 + error_log /var/log/nginx/error.log error; + access_log /var/log/nginx/access.log; + + location / { + # WebSocket Header + proxy_http_version 1.1; + proxy_set_header Upgrade websocket; + proxy_set_header Connection "Upgrade"; + + # 将客户端的 Host 和 IP 信息一并转发到对应节点 + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + + # 客户端与服务端无交互 60s 后自动断开连接,请根据实际业务场景设置 + proxy_read_timeout 60s ; + + # 执行代理访问真实服务器 + proxy_pass http://message/; + } +} \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..56a03d9 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,10 @@ +# Magic behaviour with __get, __set, __call and __callStatic is not exactly static analyser-friendly :) +# Fortunately, You can ingore it by the following config. +# +# vendor/bin/phpstan analyse app --memory-limit 200M -l 0 +# +parameters: + reportUnmatchedIgnoredErrors: false + ignoreErrors: + - '#Static call to instance method Hyperf\\HttpServer\\Router\\Router::[a-zA-Z0-9\\_]+\(\)#' + - '#Static call to instance method Hyperf\\DbConnection\\Db::[a-zA-Z0-9\\_]+\(\)#' diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..2e5c039 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + + ./test + + + + + ./app + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..e69de29 diff --git a/public/static/js/vue.global.js b/public/static/js/vue.global.js new file mode 100644 index 0000000..db18856 --- /dev/null +++ b/public/static/js/vue.global.js @@ -0,0 +1,16159 @@ +var Vue = (function (exports) { + 'use strict'; + + /** + * Make a map and return a function for checking if a key + * is in that map. + * IMPORTANT: all calls of this function must be prefixed with + * \/\*#\_\_PURE\_\_\*\/ + * So that rollup can tree-shake them if necessary. + */ + function makeMap(str, expectsLowerCase) { + const map = Object.create(null); + const list = str.split(','); + for (let i = 0; i < list.length; i++) { + map[list[i]] = true; + } + return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val]; + } + + /** + * dev only flag -> name mapping + */ + const PatchFlagNames = { + [1 /* PatchFlags.TEXT */]: `TEXT`, + [2 /* PatchFlags.CLASS */]: `CLASS`, + [4 /* PatchFlags.STYLE */]: `STYLE`, + [8 /* PatchFlags.PROPS */]: `PROPS`, + [16 /* PatchFlags.FULL_PROPS */]: `FULL_PROPS`, + [32 /* PatchFlags.HYDRATE_EVENTS */]: `HYDRATE_EVENTS`, + [64 /* PatchFlags.STABLE_FRAGMENT */]: `STABLE_FRAGMENT`, + [128 /* PatchFlags.KEYED_FRAGMENT */]: `KEYED_FRAGMENT`, + [256 /* PatchFlags.UNKEYED_FRAGMENT */]: `UNKEYED_FRAGMENT`, + [512 /* PatchFlags.NEED_PATCH */]: `NEED_PATCH`, + [1024 /* PatchFlags.DYNAMIC_SLOTS */]: `DYNAMIC_SLOTS`, + [2048 /* PatchFlags.DEV_ROOT_FRAGMENT */]: `DEV_ROOT_FRAGMENT`, + [-1 /* PatchFlags.HOISTED */]: `HOISTED`, + [-2 /* PatchFlags.BAIL */]: `BAIL` + }; + + /** + * Dev only + */ + const slotFlagsText = { + [1 /* SlotFlags.STABLE */]: 'STABLE', + [2 /* SlotFlags.DYNAMIC */]: 'DYNAMIC', + [3 /* SlotFlags.FORWARDED */]: 'FORWARDED' + }; + + const GLOBALS_WHITE_LISTED = 'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' + + 'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' + + 'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'; + const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED); + + const range = 2; + function generateCodeFrame(source, start = 0, end = source.length) { + // Split the content into individual lines but capture the newline sequence + // that separated each line. This is important because the actual sequence is + // needed to properly take into account the full line length for offset + // comparison + let lines = source.split(/(\r?\n)/); + // Separate the lines and newline sequences into separate arrays for easier referencing + const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); + lines = lines.filter((_, idx) => idx % 2 === 0); + let count = 0; + const res = []; + for (let i = 0; i < lines.length; i++) { + count += + lines[i].length + + ((newlineSequences[i] && newlineSequences[i].length) || 0); + if (count >= start) { + for (let j = i - range; j <= i + range || end > count; j++) { + if (j < 0 || j >= lines.length) + continue; + const line = j + 1; + res.push(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`); + const lineLength = lines[j].length; + const newLineSeqLength = (newlineSequences[j] && newlineSequences[j].length) || 0; + if (j === i) { + // push underline + const pad = start - (count - (lineLength + newLineSeqLength)); + const length = Math.max(1, end > count ? lineLength - pad : end - start); + res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length)); + } + else if (j > i) { + if (end > count) { + const length = Math.max(Math.min(end - count, lineLength), 1); + res.push(` | ` + '^'.repeat(length)); + } + count += lineLength + newLineSeqLength; + } + } + break; + } + } + return res.join('\n'); + } + + function normalizeStyle(value) { + if (isArray(value)) { + const res = {}; + for (let i = 0; i < value.length; i++) { + const item = value[i]; + const normalized = isString(item) + ? parseStringStyle(item) + : normalizeStyle(item); + if (normalized) { + for (const key in normalized) { + res[key] = normalized[key]; + } + } + } + return res; + } + else if (isString(value)) { + return value; + } + else if (isObject(value)) { + return value; + } + } + const listDelimiterRE = /;(?![^(]*\))/g; + const propertyDelimiterRE = /:([^]+)/; + const styleCommentRE = /\/\*.*?\*\//gs; + function parseStringStyle(cssText) { + const ret = {}; + cssText + .replace(styleCommentRE, '') + .split(listDelimiterRE) + .forEach(item => { + if (item) { + const tmp = item.split(propertyDelimiterRE); + tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); + } + }); + return ret; + } + function normalizeClass(value) { + let res = ''; + if (isString(value)) { + res = value; + } + else if (isArray(value)) { + for (let i = 0; i < value.length; i++) { + const normalized = normalizeClass(value[i]); + if (normalized) { + res += normalized + ' '; + } + } + } + else if (isObject(value)) { + for (const name in value) { + if (value[name]) { + res += name + ' '; + } + } + } + return res.trim(); + } + function normalizeProps(props) { + if (!props) + return null; + let { class: klass, style } = props; + if (klass && !isString(klass)) { + props.class = normalizeClass(klass); + } + if (style) { + props.style = normalizeStyle(style); + } + return props; + } + + // These tag configs are shared between compiler-dom and runtime-dom, so they + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element + const HTML_TAGS = 'html,body,base,head,link,meta,style,title,address,article,aside,footer,' + + 'header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,' + + 'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,' + + 'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,' + + 'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,' + + 'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,' + + 'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,' + + 'option,output,progress,select,textarea,details,dialog,menu,' + + 'summary,template,blockquote,iframe,tfoot'; + // https://developer.mozilla.org/en-US/docs/Web/SVG/Element + const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + + 'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + + 'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + + 'feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + + 'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + + 'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + + 'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + + 'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + + 'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + + 'text,textPath,title,tspan,unknown,use,view'; + const VOID_TAGS = 'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'; + /** + * Compiler only. + * Do NOT use in runtime code paths unless behind `true` flag. + */ + const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS); + /** + * Compiler only. + * Do NOT use in runtime code paths unless behind `true` flag. + */ + const isSVGTag = /*#__PURE__*/ makeMap(SVG_TAGS); + /** + * Compiler only. + * Do NOT use in runtime code paths unless behind `true` flag. + */ + const isVoidTag = /*#__PURE__*/ makeMap(VOID_TAGS); + + /** + * On the client we only need to offer special cases for boolean attributes that + * have different names from their corresponding dom properties: + * - itemscope -> N/A + * - allowfullscreen -> allowFullscreen + * - formnovalidate -> formNoValidate + * - ismap -> isMap + * - nomodule -> noModule + * - novalidate -> noValidate + * - readonly -> readOnly + */ + const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; + const isSpecialBooleanAttr = /*#__PURE__*/ makeMap(specialBooleanAttrs); + /** + * Boolean attributes should be included if the value is truthy or ''. + * e.g. `