mirror of
https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git
synced 2025-12-25 17:07:49 +08:00
first commit
This commit is contained in:
52
.devcontainer/Dockerfile
Normal file
52
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,52 @@
|
||||
# Dev Container Dockerfile
|
||||
#
|
||||
# @link https://www.hyperf.io
|
||||
# @document https://hyperf.wiki
|
||||
# @contact group@hyperf.io
|
||||
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
|
||||
FROM hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
|
||||
|
||||
##
|
||||
# ---------- env settings ----------
|
||||
##
|
||||
# --build-arg timezone=Asia/Shanghai
|
||||
ARG timezone
|
||||
|
||||
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
|
||||
APP_ENV=dev \
|
||||
SCAN_CACHEABLE=(false)
|
||||
|
||||
# update
|
||||
RUN set -ex \
|
||||
# show php version and extensions
|
||||
&& php -v \
|
||||
&& php -m \
|
||||
&& php --ri swoole \
|
||||
# ---------- some config ----------
|
||||
&& cd /etc/php* \
|
||||
# - config PHP
|
||||
&& { \
|
||||
echo "upload_max_filesize=128M"; \
|
||||
echo "post_max_size=128M"; \
|
||||
echo "memory_limit=1G"; \
|
||||
echo "date.timezone=${TIMEZONE}"; \
|
||||
} | tee conf.d/99_overrides.ini \
|
||||
# - config timezone
|
||||
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
|
||||
&& echo "${TIMEZONE}" > /etc/timezone \
|
||||
# ---------- clear works ----------
|
||||
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
|
||||
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
|
||||
|
||||
WORKDIR /opt/www
|
||||
|
||||
# Composer Cache
|
||||
# COPY ./composer.* /opt/www/
|
||||
# RUN composer install --no-dev --no-scripts
|
||||
|
||||
COPY . /opt/www
|
||||
RUN composer install && php bin/hyperf.php
|
||||
|
||||
EXPOSE 9501
|
||||
7
.devcontainer/devcontainer.json
Normal file
7
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"build": {
|
||||
"context": "..",
|
||||
"dockerfile": "./Dockerfile"
|
||||
},
|
||||
"forwardPorts": [9501]
|
||||
}
|
||||
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
**
|
||||
!app/
|
||||
!bin/
|
||||
!config/
|
||||
!composer.*
|
||||
22
.env.example
Normal file
22
.env.example
Normal file
@@ -0,0 +1,22 @@
|
||||
APP_NAME=sfyy
|
||||
APP_ENV=dev
|
||||
APP_DEBUG=false
|
||||
|
||||
APP_URL=http://127.0.0.1:9501
|
||||
|
||||
JWT_SECRET=azOVxsOWt3r0ozZNz8Ss429ht0T8z6OpeIJAIwNp6X0xqrbEY2epfIWyxtC1qSNM8eD6/LQ/SahcQi2ByXa/2A==
|
||||
|
||||
DB_DRIVER=mysql
|
||||
DB_HOST=s2.gnip.vip
|
||||
DB_PORT=20191
|
||||
DB_DATABASE=mineadmin
|
||||
DB_USERNAME=hhl
|
||||
DB_PASSWORD=hhltest
|
||||
DB_CHARSET=utf8mb4
|
||||
DB_COLLATION=utf8mb4_unicode_ci
|
||||
DB_PREFIX=
|
||||
|
||||
REDIS_HOST=s2.gnip.vip
|
||||
REDIS_AUTH=hhltest
|
||||
REDIS_PORT=4379
|
||||
REDIS_DB=0
|
||||
54
.github/workflows/Dockerfile
vendored
Normal file
54
.github/workflows/Dockerfile
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# Default Dockerfile
|
||||
#
|
||||
# @link https://www.hyperf.io
|
||||
# @document https://hyperf.wiki
|
||||
# @contact group@hyperf.io
|
||||
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
|
||||
FROM hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
|
||||
|
||||
##
|
||||
# ---------- env settings ----------
|
||||
##
|
||||
# --build-arg timezone=Asia/Shanghai
|
||||
ARG timezone
|
||||
|
||||
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
|
||||
APP_ENV=prod \
|
||||
SCAN_CACHEABLE=(true)
|
||||
|
||||
# update
|
||||
RUN set -ex \
|
||||
# show php version and extensions
|
||||
&& php -v \
|
||||
&& php -m \
|
||||
&& php --ri swoole \
|
||||
# ---------- some config ----------
|
||||
&& cd /etc/php* \
|
||||
# - config PHP
|
||||
&& { \
|
||||
echo "upload_max_filesize=128M"; \
|
||||
echo "post_max_size=128M"; \
|
||||
echo "memory_limit=1G"; \
|
||||
echo "date.timezone=${TIMEZONE}"; \
|
||||
} | tee conf.d/99_overrides.ini \
|
||||
# - config timezone
|
||||
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
|
||||
&& echo "${TIMEZONE}" > /etc/timezone \
|
||||
# ---------- clear works ----------
|
||||
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
|
||||
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
|
||||
|
||||
WORKDIR /opt/www
|
||||
|
||||
# Composer Cache
|
||||
# COPY ./composer.* /opt/www/
|
||||
# RUN composer install --no-dev --no-scripts
|
||||
|
||||
COPY . /opt/www
|
||||
RUN print "\n" | composer install -o && php bin/hyperf.php
|
||||
|
||||
EXPOSE 9501
|
||||
|
||||
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
|
||||
12
.github/workflows/build.yml
vendored
Normal file
12
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: Build Docker
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: cp -rf .github/workflows/Dockerfile . && docker build -t hyperf .
|
||||
25
.github/workflows/release.yml
vendored
Normal file
25
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
name: Release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
.buildpath
|
||||
.settings/
|
||||
.project
|
||||
*.patch
|
||||
.idea/
|
||||
.git/
|
||||
runtime/
|
||||
vendor/
|
||||
.phpintel/
|
||||
.env
|
||||
.DS_Store
|
||||
.phpunit*
|
||||
*.cache
|
||||
.vscode/
|
||||
/phpstan.neon
|
||||
/phpunit.xml
|
||||
57
.gitlab-ci.yml
Normal file
57
.gitlab-ci.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
# usermod -aG docker gitlab-runner
|
||||
|
||||
stages:
|
||||
- build
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
PROJECT_NAME: hyperf
|
||||
REGISTRY_URL: registry-docker.org
|
||||
|
||||
build_test_docker:
|
||||
stage: build
|
||||
before_script:
|
||||
# - git submodule sync --recursive
|
||||
# - git submodule update --init --recursive
|
||||
script:
|
||||
- docker build . -t $PROJECT_NAME
|
||||
- docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:test
|
||||
- docker push $REGISTRY_URL/$PROJECT_NAME:test
|
||||
only:
|
||||
- test
|
||||
tags:
|
||||
- builder
|
||||
|
||||
deploy_test_docker:
|
||||
stage: deploy
|
||||
script:
|
||||
- docker stack deploy -c deploy.test.yml --with-registry-auth $PROJECT_NAME
|
||||
only:
|
||||
- test
|
||||
tags:
|
||||
- test
|
||||
|
||||
build_docker:
|
||||
stage: build
|
||||
before_script:
|
||||
# - git submodule sync --recursive
|
||||
# - git submodule update --init --recursive
|
||||
script:
|
||||
- docker build . -t $PROJECT_NAME
|
||||
- docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME
|
||||
- docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:latest
|
||||
- docker push $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME
|
||||
- docker push $REGISTRY_URL/$PROJECT_NAME:latest
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- builder
|
||||
|
||||
deploy_docker:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo SUCCESS
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- builder
|
||||
106
.php-cs-fixer.php
Normal file
106
.php-cs-fixer.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
$header = <<<'EOF'
|
||||
This file is part of Hyperf.
|
||||
|
||||
@link https://www.hyperf.io
|
||||
@document https://hyperf.wiki
|
||||
@contact group@hyperf.io
|
||||
@license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
EOF;
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'@Symfony' => true,
|
||||
'@DoctrineAnnotation' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'header_comment' => [
|
||||
'comment_type' => 'PHPDoc',
|
||||
'header' => $header,
|
||||
'separate' => 'none',
|
||||
'location' => 'after_declare_strict',
|
||||
],
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'list_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one',
|
||||
],
|
||||
'global_namespace_import' => [
|
||||
'import_classes' => true,
|
||||
'import_constants' => true,
|
||||
'import_functions' => null,
|
||||
],
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare',
|
||||
],
|
||||
],
|
||||
'general_phpdoc_annotation_remove' => [
|
||||
'annotations' => [
|
||||
'author',
|
||||
],
|
||||
],
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class', 'function', 'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'single_line_comment_style' => [
|
||||
'comment_types' => [
|
||||
],
|
||||
],
|
||||
'yoda_style' => [
|
||||
'always_move_variable' => false,
|
||||
'equal' => false,
|
||||
'identical' => false,
|
||||
],
|
||||
'phpdoc_align' => [
|
||||
'align' => 'left',
|
||||
],
|
||||
'multiline_whitespace_before_semicolons' => [
|
||||
'strategy' => 'no_multi_line',
|
||||
],
|
||||
'constant_case' => [
|
||||
'case' => 'lower',
|
||||
],
|
||||
'class_attributes_separation' => true,
|
||||
'combine_consecutive_unsets' => true,
|
||||
'declare_strict_types' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_unused_imports' => true,
|
||||
'not_operator_with_successor_space' => true,
|
||||
'not_operator_with_space' => false,
|
||||
'ordered_class_elements' => true,
|
||||
'php_unit_strict' => false,
|
||||
'phpdoc_separation' => false,
|
||||
'single_quote' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'multiline_comment_opening_closing' => true,
|
||||
'single_line_empty_body' => false,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->exclude('public')
|
||||
->exclude('runtime')
|
||||
->exclude('vendor')
|
||||
->in(__DIR__)
|
||||
)
|
||||
->setUsingCache(false);
|
||||
12
.phpstorm.meta.php
Normal file
12
.phpstorm.meta.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PHPSTORM_META {
|
||||
// Reflect
|
||||
override(\Psr\Container\ContainerInterface::get(0), map(['' => '@']));
|
||||
override(\Hyperf\Context\Context::get(0), map(['' => '@']));
|
||||
override(\make(0), map(['' => '@']));
|
||||
override(\di(0), map(['' => '@']));
|
||||
override(\Hyperf\Support\make(0), map(['' => '@']));
|
||||
override(\Hyperf\Support\optional(0), type(0));
|
||||
override(\Hyperf\Tappable\tap(0), type(0));
|
||||
}
|
||||
63
BLADE.README.md
Normal file
63
BLADE.README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Introduction
|
||||
|
||||
This is a skeleton application using the Hyperf framework. This application is meant to be used as a starting place for those looking to get their feet wet with Hyperf Framework.
|
||||
|
||||
# Requirements
|
||||
|
||||
Hyperf has some requirements for the system environment, it can only run under Linux and Mac environment, but due to the development of Docker virtualization technology, Docker for Windows can also be used as the running environment under Windows.
|
||||
|
||||
The various versions of Dockerfile have been prepared for you in the [hyperf/hyperf-docker](https://github.com/hyperf/hyperf-docker) project, or directly based on the already built [hyperf/hyperf](https://hub.docker.com/r/hyperf/hyperf) Image to run.
|
||||
|
||||
When you don't want to use Docker as the basis for your running environment, you need to make sure that your operating environment meets the following requirements:
|
||||
|
||||
- PHP >= 8.1
|
||||
- Any of the following network engines
|
||||
- Swoole PHP extension >= 5.0,with `swoole.use_shortname` set to `Off` in your `php.ini`
|
||||
- Swow PHP extension >= 1.3
|
||||
- JSON PHP extension
|
||||
- Pcntl PHP extension
|
||||
- OpenSSL PHP extension (If you need to use the HTTPS)
|
||||
- PDO PHP extension (If you need to use the MySQL Client)
|
||||
- Redis PHP extension (If you need to use the Redis Client)
|
||||
- Protobuf PHP extension (If you need to use the gRPC Server or Client)
|
||||
|
||||
# Installation using Composer
|
||||
|
||||
The easiest way to create a new Hyperf project is to use [Composer](https://getcomposer.org/). If you don't have it already installed, then please install as per [the documentation](https://getcomposer.org/download/).
|
||||
|
||||
To create your new Hyperf project:
|
||||
|
||||
```bash
|
||||
composer create-project hyperf/hyperf-skeleton path/to/install
|
||||
```
|
||||
|
||||
If your development environment is based on Docker you can use the official Composer image to create a new Hyperf project:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v $(pwd):/app composer create-project --ignore-platform-reqs hyperf/hyperf-skeleton path/to/install
|
||||
```
|
||||
|
||||
# Getting started
|
||||
|
||||
Once installed, you can run the server immediately using the command below.
|
||||
|
||||
```bash
|
||||
cd path/to/install
|
||||
php bin/hyperf.php start
|
||||
```
|
||||
|
||||
Or if in a Docker based environment you can use the `docker-compose.yml` provided by the template:
|
||||
|
||||
```bash
|
||||
cd path/to/install
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
This will start the cli-server on port `9501`, and bind it to all network interfaces. You can then visit the site at `http://localhost:9501/` which will bring up Hyperf default home page.
|
||||
|
||||
## Hints
|
||||
|
||||
- A nice tip is to rename `hyperf-skeleton` of files like `composer.json` and `docker-compose.yml` to your actual project name.
|
||||
- Take a look at `config/routes.php` and `app/Controller/IndexController.php` to see an example of a HTTP entrypoint.
|
||||
|
||||
**Remember:** you can always replace the contents of this README.md file to something that fits your project description.
|
||||
54
Dockerfile
Normal file
54
Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
||||
# Default Dockerfile
|
||||
#
|
||||
# @link https://www.hyperf.io
|
||||
# @document https://hyperf.wiki
|
||||
# @contact group@hyperf.io
|
||||
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
|
||||
FROM hyperf/hyperf:8.3-alpine-v3.19-swoole
|
||||
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
|
||||
|
||||
##
|
||||
# ---------- env settings ----------
|
||||
##
|
||||
# --build-arg timezone=Asia/Shanghai
|
||||
ARG timezone
|
||||
|
||||
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
|
||||
APP_ENV=prod \
|
||||
SCAN_CACHEABLE=(true)
|
||||
|
||||
# update
|
||||
RUN set -ex \
|
||||
# show php version and extensions
|
||||
&& php -v \
|
||||
&& php -m \
|
||||
&& php --ri swoole \
|
||||
# ---------- some config ----------
|
||||
&& cd /etc/php* \
|
||||
# - config PHP
|
||||
&& { \
|
||||
echo "upload_max_filesize=128M"; \
|
||||
echo "post_max_size=128M"; \
|
||||
echo "memory_limit=1G"; \
|
||||
echo "date.timezone=${TIMEZONE}"; \
|
||||
} | tee conf.d/99_overrides.ini \
|
||||
# - config timezone
|
||||
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
|
||||
&& echo "${TIMEZONE}" > /etc/timezone \
|
||||
# ---------- clear works ----------
|
||||
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
|
||||
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
|
||||
|
||||
WORKDIR /opt/www
|
||||
|
||||
# Composer Cache
|
||||
# COPY ./composer.* /opt/www/
|
||||
# RUN composer install --no-dev --no-scripts
|
||||
|
||||
COPY . /opt/www
|
||||
RUN composer install --no-dev -o && php bin/hyperf.php
|
||||
|
||||
EXPOSE 9501
|
||||
|
||||
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Hyperf
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
87
README.md
Normal file
87
README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
## 仓库
|
||||
|
||||
- [sfyy_server](https://codeup.aliyun.com/67039465d8d1ada68263f984/hhl/rewrite/hyperf_service.git) - git远程仓库地址
|
||||
|
||||
## 特性
|
||||
|
||||
- **最新技术栈**:使用 PHP8.3/hyperf3.1/swoole5.1.4/phpredis 6.0.2 等后端前沿技术开发
|
||||
|
||||
## 文档
|
||||
|
||||
[文档地址 Github](https://hyperf.wiki/3.1/)
|
||||
|
||||
## 前序准备
|
||||
|
||||
- [php8.3](https://www.php.net/) 和 [git](https://git-scm.com/) - 项目开发环境
|
||||
- [swoole](https://www.swoole.com/) - 熟悉 swoole 特性
|
||||
- [php8.3](https://www.php.net/) - 熟悉 php 基础语法
|
||||
- [hyperf](https://hyperf.wiki/3.1/) - 熟悉 `hyperf` 基本语法
|
||||
## 安装和使用
|
||||
|
||||
- 安装 swoole 和 phpredis 扩展
|
||||
|
||||
```
|
||||
自行搜索安装教程
|
||||
```
|
||||
|
||||
- 获取代码
|
||||
|
||||
```bash
|
||||
git clone https://codeup.aliyun.com/67039465d8d1ada68263f984/hhl/rewrite/hyperf_service.git
|
||||
|
||||
mkdir uploads
|
||||
```
|
||||
|
||||
- vendor
|
||||
|
||||
```bash
|
||||
composer install
|
||||
```
|
||||
|
||||
- 运行
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
|
||||
vim .env
|
||||
|
||||
php bin/hyperf.php start
|
||||
```
|
||||
|
||||
- command 函数
|
||||
```bash
|
||||
#框架自有
|
||||
php bin/hyperf.php gen:controller LoginController
|
||||
php bin/hyperf.php gen:model UserModel
|
||||
php bin/hyperf.php gen:request LoginRequest
|
||||
php bin/hyperf.php gen:command TestCommand
|
||||
php bin/hyperf.php gen:job TestJob
|
||||
php bin/hyperf.php gen:listener TestListener
|
||||
php bin/hyperf.php gen:middleware AuthMiddleware
|
||||
php bin/hyperf.php gen:amqp-consumer DemoConsumer
|
||||
php bin/hyperf.php gen:amqp-producer DemoProducer
|
||||
php bin/hyperf.php gen:constant ErrorCode --type enum
|
||||
|
||||
#新增命令
|
||||
php bin/hyperf.php gen:service LoginService
|
||||
php bin/hyperf.php gen:cron OssTask
|
||||
php bin/hyperf.php gen:event TestEvent
|
||||
```
|
||||
|
||||
## Git 贡献提交规范
|
||||
|
||||
- `feat` 新功能
|
||||
- `fix` 修补 bug
|
||||
- `docs` 文档
|
||||
- `style` 格式、样式(不影响代码运行的变动)
|
||||
- `refactor` 重构(即不是新增功能,也不是修改 BUG 的代码)
|
||||
- `perf` 优化相关,比如提升性能、体验
|
||||
- `test` 添加测试
|
||||
- `build` 编译相关的修改,对项目构建或者依赖的改动
|
||||
- `ci` 持续集成修改
|
||||
- `chore` 构建过程或辅助工具的变动
|
||||
- `revert` 回滚到上一个版本
|
||||
- `workflow` 工作流改进
|
||||
- `mod` 不确定分类的修改
|
||||
- `wip` 开发中
|
||||
- `types` 类型
|
||||
14
app/Annotation/ResponseFormat.php
Normal file
14
app/Annotation/ResponseFormat.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Annotation;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
|
||||
class ResponseFormat
|
||||
{
|
||||
/**
|
||||
* @param string $format
|
||||
*/
|
||||
public function __construct(public string $format) {}
|
||||
}
|
||||
51
app/Aspect/ResponseFormatAspect.php
Normal file
51
app/Aspect/ResponseFormatAspect.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Aspect;
|
||||
|
||||
use App\Annotation\ResponseFormat;
|
||||
use Hyperf\Di\Annotation\Aspect;
|
||||
use Hyperf\Di\Aop\AbstractAspect;
|
||||
use Hyperf\Di\Exception\Exception;
|
||||
use Hyperf\HttpServer\Request;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Hyperf\Di\Aop\ProceedingJoinPoint;
|
||||
|
||||
#[Aspect]
|
||||
class ResponseFormatAspect extends AbstractAspect
|
||||
{
|
||||
/**
|
||||
* @var array|class-string[]
|
||||
*/
|
||||
public array $annotations = [
|
||||
ResponseFormat::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(protected ContainerInterface $container) {}
|
||||
|
||||
/**
|
||||
* @param ProceedingJoinPoint $proceedingJoinPoint
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed
|
||||
{
|
||||
// 获取注解定义的格式
|
||||
$annotation = $proceedingJoinPoint->getAnnotationMetadata()->class[ResponseFormat::class]
|
||||
?? $proceedingJoinPoint->getAnnotationMetadata()->method[ResponseFormat::class] ?? null;
|
||||
|
||||
if ($annotation) {
|
||||
// 将注解格式存入请求属性(覆盖中间件的默认值)
|
||||
$request = $proceedingJoinPoint->arguments['request'] ?? null;
|
||||
if ($request instanceof Request) {
|
||||
$request->withAttribute('response_format', $annotation->format);
|
||||
}
|
||||
}
|
||||
|
||||
return $proceedingJoinPoint->process();
|
||||
}
|
||||
}
|
||||
51
app/Command/GenClass/RepositoryGenCommand.php
Normal file
51
app/Command/GenClass/RepositoryGenCommand.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command\GenClass;
|
||||
|
||||
use Hyperf\Command\Command as HyperfCommand;
|
||||
use Hyperf\Command\Annotation\Command;
|
||||
use Hyperf\Devtool\Generator\GeneratorCommand;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
#[Command]
|
||||
class RepositoryGenCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(protected ContainerInterface $container)
|
||||
{
|
||||
parent::__construct('gen:repository');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setDescription('Create a new repository class');
|
||||
|
||||
$this->setHelp('php bin/hyperf.php gen:repository fileRepository');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 stubs
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__ . '/stubs/repository.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认命名空间
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace(): string
|
||||
{
|
||||
return 'App\\Repository';
|
||||
}
|
||||
}
|
||||
50
app/Command/GenClass/ServiceGenCommand.php
Normal file
50
app/Command/GenClass/ServiceGenCommand.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command\GenClass;
|
||||
|
||||
use Hyperf\Command\Annotation\Command;
|
||||
use Hyperf\Devtool\Generator\GeneratorCommand;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
#[Command]
|
||||
class ServiceGenCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(protected ContainerInterface $container)
|
||||
{
|
||||
parent::__construct('gen:service');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setDescription('Create a new service class');
|
||||
|
||||
$this->setHelp('php bin/hyperf.php gen:service /folder/fileService');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 stubs
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__ . '/stubs/service.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认命名空间
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace(): string
|
||||
{
|
||||
return 'App\\Service';
|
||||
}
|
||||
}
|
||||
16
app/Command/GenClass/stubs/repository.stub
Normal file
16
app/Command/GenClass/stubs/repository.stub
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace %NAMESPACE%;
|
||||
|
||||
final class %CLASS% extends BaseRepository
|
||||
{
|
||||
public function __construct(protected readonly xxx $model) {}
|
||||
}
|
||||
19
app/Command/GenClass/stubs/service.stub
Normal file
19
app/Command/GenClass/stubs/service.stub
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace %NAMESPACE%;
|
||||
|
||||
class %CLASS% extends
|
||||
{
|
||||
public function handle()
|
||||
{
|
||||
//todo Write logic
|
||||
}
|
||||
}
|
||||
18
app/Common/Macros/BlueprintMacros.php
Normal file
18
app/Common/Macros/BlueprintMacros.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Macros;
|
||||
|
||||
use Hyperf\Database\Schema\Blueprint;
|
||||
|
||||
class BlueprintMacros
|
||||
{
|
||||
public static function register(): void
|
||||
{
|
||||
Blueprint::macro('authorFields', function () {
|
||||
/** @var Blueprint $this */
|
||||
$this->unsignedBigInteger('created_by')->nullable()->comment('创建人ID');
|
||||
$this->unsignedBigInteger('updated_by')->nullable()->comment('更新人ID');
|
||||
return $this;
|
||||
});
|
||||
}
|
||||
}
|
||||
19
app/Constants/AdminCode.php
Normal file
19
app/Constants/AdminCode.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants;
|
||||
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
use Hyperf\Constants\Annotation\Message;
|
||||
use Hyperf\Constants\EnumConstantsTrait;
|
||||
|
||||
#[Constants]
|
||||
final class AdminCode extends ResultCode
|
||||
{
|
||||
#[Message("登录失败")]
|
||||
public const int LOGIN_ERROR = 10001;
|
||||
|
||||
#[Message("验证已过期")]
|
||||
public const int LOGIN_TOKEN_ERROR = 10002;
|
||||
}
|
||||
25
app/Constants/ErrorCode.php
Normal file
25
app/Constants/ErrorCode.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Constants;
|
||||
|
||||
use Hyperf\Constants\AbstractConstants;
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
|
||||
#[Constants]
|
||||
class ErrorCode extends AbstractConstants
|
||||
{
|
||||
/**
|
||||
* @Message("Server Error!")
|
||||
*/
|
||||
public const SERVER_ERROR = 500;
|
||||
}
|
||||
32
app/Constants/Model/AdminUser/AdminMenuStatusCode.php
Normal file
32
app/Constants/Model/AdminUser/AdminMenuStatusCode.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Model\AdminUser;
|
||||
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
use Hyperf\Constants\Annotation\Message;
|
||||
use Hyperf\Constants\EnumConstantsTrait;
|
||||
|
||||
#[Constants]
|
||||
enum AdminMenuStatusCode: int
|
||||
{
|
||||
use EnumConstantsTrait;
|
||||
|
||||
|
||||
#[Message('正常')]
|
||||
case Normal = 1;
|
||||
|
||||
#[Message('停用')]
|
||||
case DISABLE = 2;
|
||||
|
||||
public function isNormal(): bool
|
||||
{
|
||||
return $this === self::Normal;
|
||||
}
|
||||
|
||||
public function isDisable(): bool
|
||||
{
|
||||
return $this === self::DISABLE;
|
||||
}
|
||||
}
|
||||
32
app/Constants/Model/AdminUser/AdminRoleStatusCode.php
Normal file
32
app/Constants/Model/AdminUser/AdminRoleStatusCode.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Model\AdminUser;
|
||||
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
use Hyperf\Constants\Annotation\Message;
|
||||
use Hyperf\Constants\EnumConstantsTrait;
|
||||
|
||||
#[Constants]
|
||||
enum AdminRoleStatusCode: int
|
||||
{
|
||||
use EnumConstantsTrait;
|
||||
|
||||
|
||||
#[Message('正常')]
|
||||
case Normal = 1;
|
||||
|
||||
#[Message('停用')]
|
||||
case DISABLE = 2;
|
||||
|
||||
public function isNormal(): bool
|
||||
{
|
||||
return $this === self::Normal;
|
||||
}
|
||||
|
||||
public function isDisable(): bool
|
||||
{
|
||||
return $this === self::DISABLE;
|
||||
}
|
||||
}
|
||||
31
app/Constants/Model/AdminUser/AdminUserStatusCode.php
Normal file
31
app/Constants/Model/AdminUser/AdminUserStatusCode.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Model\AdminUser;
|
||||
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
use Hyperf\Constants\Annotation\Message;
|
||||
use Hyperf\Constants\EnumConstantsTrait;
|
||||
|
||||
#[Constants]
|
||||
enum AdminUserStatusCode: int
|
||||
{
|
||||
use EnumConstantsTrait;
|
||||
|
||||
#[Message('正常')]
|
||||
case Normal = 1;
|
||||
|
||||
#[Message('停用')]
|
||||
case DISABLE = 2;
|
||||
|
||||
public function isNormal(): bool
|
||||
{
|
||||
return $this === self::Normal;
|
||||
}
|
||||
|
||||
public function isDisable(): bool
|
||||
{
|
||||
return $this === self::DISABLE;
|
||||
}
|
||||
}
|
||||
21
app/Constants/Model/AdminUser/AdminUserTypeCode.php
Normal file
21
app/Constants/Model/AdminUser/AdminUserTypeCode.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Model\AdminUser;
|
||||
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
use Hyperf\Constants\Annotation\Message;
|
||||
use Hyperf\Constants\EnumConstantsTrait;
|
||||
|
||||
#[Constants]
|
||||
enum AdminUserTypeCode: int
|
||||
{
|
||||
use EnumConstantsTrait;
|
||||
|
||||
#[Message('系统用户')]
|
||||
case SYSTEM = 100;
|
||||
|
||||
#[Message('普通用户')]
|
||||
case USER = 200;
|
||||
}
|
||||
22
app/Constants/ResultCode.php
Normal file
22
app/Constants/ResultCode.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants;
|
||||
|
||||
use Hyperf\Constants\AbstractConstants;
|
||||
use Hyperf\Constants\Annotation\Constants;
|
||||
use Hyperf\Constants\Annotation\Message;
|
||||
use Hyperf\Constants\EnumConstantsTrait;
|
||||
|
||||
#[Constants]
|
||||
class ResultCode extends AbstractConstants
|
||||
{
|
||||
use EnumConstantsTrait;
|
||||
|
||||
#[Message("success")]
|
||||
final public const int SUCCESS = 0;
|
||||
|
||||
#[Message("failed")]
|
||||
final public const int ERROR = 1;
|
||||
}
|
||||
30
app/Controller/AbstractController.php
Normal file
30
app/Controller/AbstractController.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Hyperf\HttpServer\Contract\ResponseInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
abstract class AbstractController
|
||||
{
|
||||
#[Inject]
|
||||
protected ContainerInterface $container;
|
||||
|
||||
#[Inject]
|
||||
protected RequestInterface $request;
|
||||
|
||||
#[Inject]
|
||||
protected ResponseInterface $response;
|
||||
}
|
||||
25
app/Controller/Admin/LoginController.php
Normal file
25
app/Controller/Admin/LoginController.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Annotation\ResponseFormat;
|
||||
use App\Controller\AbstractController;
|
||||
use App\Request\Admin\LoginRequest;
|
||||
use App\Service\Admin\Login\LoginService;
|
||||
use Hyperf\HttpServer\Annotation\Controller;
|
||||
use Hyperf\HttpServer\Annotation\RequestMapping;
|
||||
use Hyperf\Validation\Annotation\Scene;
|
||||
|
||||
#[Controller(prefix: "admin/login")]
|
||||
#[ResponseFormat('admin')]
|
||||
final class LoginController extends AbstractController
|
||||
{
|
||||
#[RequestMapping(path: "login", methods: "POST")]
|
||||
#[Scene(scene: "login")]
|
||||
public function login(LoginRequest $request)
|
||||
{
|
||||
return (new LoginService)->handle();
|
||||
}
|
||||
}
|
||||
27
app/Controller/IndexController.php
Normal file
27
app/Controller/IndexController.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
class IndexController extends AbstractController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = $this->request->input('user', 'Hyperf');
|
||||
$method = $this->request->getMethod();
|
||||
|
||||
return [
|
||||
'method' => $method,
|
||||
'message' => "Hello {$user}.",
|
||||
];
|
||||
}
|
||||
}
|
||||
29
app/Exception/BusinessException.php
Normal file
29
app/Exception/BusinessException.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
use App\Constants\ErrorCode;
|
||||
use Hyperf\Server\Exception\ServerException;
|
||||
use Throwable;
|
||||
|
||||
class BusinessException extends ServerException
|
||||
{
|
||||
public function __construct(int $code = 0, string $message = null, Throwable $previous = null)
|
||||
{
|
||||
if (is_null($message)) {
|
||||
$message = ErrorCode::getMessage($code);
|
||||
}
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
14
app/Exception/ErrException.php
Normal file
14
app/Exception/ErrException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
use App\Constants\ResultCode;
|
||||
use Hyperf\Server\Exception\ServerException;
|
||||
|
||||
class ErrException extends ServerException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = ResultCode::ERROR;
|
||||
}
|
||||
38
app/Exception/Handler/AppExceptionHandler.php
Normal file
38
app/Exception/Handler/AppExceptionHandler.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Exception\Handler;
|
||||
|
||||
use Hyperf\Contract\StdoutLoggerInterface;
|
||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||
use Hyperf\HttpMessage\Stream\SwooleStream;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
class AppExceptionHandler extends ExceptionHandler
|
||||
{
|
||||
public function __construct(protected StdoutLoggerInterface $logger)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle(Throwable $throwable, ResponseInterface $response)
|
||||
{
|
||||
$this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
|
||||
$this->logger->error($throwable->getTraceAsString());
|
||||
return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
|
||||
}
|
||||
|
||||
public function isValid(Throwable $throwable): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
71
app/Exception/Handler/BaseErrExceptionHandler.php
Normal file
71
app/Exception/Handler/BaseErrExceptionHandler.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception\Handler;
|
||||
|
||||
|
||||
use App\Lib\Return\AdminReturn;
|
||||
use App\Lib\Return\ApiReturn;
|
||||
use Hyperf\ExceptionHandler\ExceptionHandler;
|
||||
use Hyperf\HttpMessage\Stream\SwooleStream;
|
||||
use Hyperf\HttpServer\Request;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
abstract class BaseErrExceptionHandler extends ExceptionHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Request $request,
|
||||
private readonly ContainerInterface $container,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param Throwable $e
|
||||
* @param ResponseInterface $response
|
||||
* @return MessageInterface|ResponseInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
protected function handlerResponse(
|
||||
Throwable $e,
|
||||
ResponseInterface $response
|
||||
): MessageInterface|ResponseInterface
|
||||
{
|
||||
// 从注解获取响应格式(优先于路径解析)
|
||||
$format = $this->request->getAttribute('response_format') ?? $this->repairResponseFormatByPath();
|
||||
|
||||
// 动态选择策略
|
||||
$returnClass = match ($format) {
|
||||
'admin', 'common' => AdminReturn::class,
|
||||
'api' => ApiReturn::class,
|
||||
default => null,
|
||||
};
|
||||
if (!$returnClass) return $response;
|
||||
|
||||
/**
|
||||
* @var AdminReturn|ApiReturn $returnObj
|
||||
*/
|
||||
$returnObj = $this->container->get($returnClass);
|
||||
$result = $returnObj->error($e->getMessage(), $e->getCode());
|
||||
$this->stopPropagation();
|
||||
return $response->withHeader("Content-Type", "application/json")
|
||||
->withStatus(200)
|
||||
->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function repairResponseFormatByPath(): string
|
||||
{
|
||||
// 兜底逻辑:根据路径前缀推断
|
||||
return match (explode('/', $this->request->path())[0] ?? '') {
|
||||
'admin', 'common' => 'admin',
|
||||
'api' => 'api',
|
||||
default => 'default',
|
||||
};
|
||||
}
|
||||
}
|
||||
35
app/Exception/Handler/ErrExceptionHandler.php
Normal file
35
app/Exception/Handler/ErrExceptionHandler.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception\Handler;
|
||||
|
||||
use App\Exception\ErrException;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
class ErrExceptionHandler extends BaseErrExceptionHandler
|
||||
{
|
||||
/**
|
||||
* @param Throwable $throwable
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
return $throwable instanceof ErrException
|
||||
? $this->handlerResponse($throwable, $response)
|
||||
: $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable $throwable
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof ErrException;
|
||||
}
|
||||
}
|
||||
35
app/Exception/Handler/ValidationExceptionHandler.php
Normal file
35
app/Exception/Handler/ValidationExceptionHandler.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception\Handler;
|
||||
|
||||
use Hyperf\Validation\ValidationException;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
class ValidationExceptionHandler extends BaseErrExceptionHandler
|
||||
{
|
||||
/**
|
||||
* @param Throwable $throwable
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
return $throwable instanceof ValidationException
|
||||
? $this->handlerResponse($throwable, $response)
|
||||
: $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable $throwable
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof ValidationException;
|
||||
}
|
||||
}
|
||||
24
app/Interface/JwtInterface.php
Normal file
24
app/Interface/JwtInterface.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Interface;
|
||||
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
|
||||
interface JwtInterface
|
||||
{
|
||||
public function builderAccessToken(string $sub, ?\Closure $callable = null): UnencryptedToken;
|
||||
|
||||
public function builderRefreshToken(string $sub, ?\Closure $callable = null): UnencryptedToken;
|
||||
|
||||
public function parserAccessToken(string $accessToken): UnencryptedToken;
|
||||
|
||||
public function parserRefreshToken(string $refreshToken): UnencryptedToken;
|
||||
|
||||
public function addBlackList(UnencryptedToken $token): bool;
|
||||
|
||||
public function hasBlackList(UnencryptedToken $token): bool;
|
||||
|
||||
public function removeBlackList(UnencryptedToken $token): bool;
|
||||
|
||||
public function getConfig(string $key, mixed $default = null): mixed;
|
||||
}
|
||||
233
app/Lib/Jwt/AbstractJwt.php
Normal file
233
app/Lib/Jwt/AbstractJwt.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use App\Interface\JwtInterface;
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Cache\CacheManager;
|
||||
use Hyperf\Cache\Driver\DriverInterface;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\JwtFacade;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\Constraint\SignedWith;
|
||||
use Lcobucci\JWT\Validation\Constraint\StrictValidAt;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
abstract class AbstractJwt implements JwtInterface
|
||||
{
|
||||
/**
|
||||
* @param array $config
|
||||
* @param CacheManager $cacheManager
|
||||
* @param Clock $clock
|
||||
* @param AccessTokenConstraint $accessTokenConstraint
|
||||
* @param RefreshTokenConstraint $refreshTokenConstraint
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $config,
|
||||
private readonly CacheManager $cacheManager,
|
||||
private readonly Clock $clock,
|
||||
private readonly AccessTokenConstraint $accessTokenConstraint,
|
||||
private readonly RefreshTokenConstraint $refreshTokenConstraint
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $sub
|
||||
* @param \Closure|null $callable
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function builderAccessToken(string $sub, ?\Closure $callable = null): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()->issue(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey(),
|
||||
function (Builder $builder, \DateTimeImmutable $immutable) use ($sub, $callable) {
|
||||
$builder = $builder->identifiedBy($sub);
|
||||
if ($callable !== null) {
|
||||
$builder = $callable($builder);
|
||||
}
|
||||
return $builder->expiresAt($this->getExpireAt($immutable));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sub
|
||||
* @param \Closure|null $callable
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function builderRefreshToken(string $sub, ?\Closure $callable = null): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()->issue(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey(),
|
||||
function (Builder $builder, \DateTimeImmutable $immutable) use ($sub, $callable) {
|
||||
$builder = $builder->identifiedBy($sub);
|
||||
$builder = $builder->expiresAt($this->getRefreshExpireAt($immutable));
|
||||
if ($callable !== null) {
|
||||
$builder = $callable($builder);
|
||||
}
|
||||
return $builder->relatedTo('refresh');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function parserAccessToken(string $accessToken): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()
|
||||
->parse(
|
||||
$accessToken,
|
||||
new SignedWith(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey()
|
||||
),
|
||||
new StrictValidAt(
|
||||
$this->clock,
|
||||
$this->clock->now()->diff($this->getExpireAt($this->clock->now()))
|
||||
),
|
||||
$this->getBlackListConstraint(),
|
||||
$this->refreshTokenConstraint
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function parserRefreshToken(string $refreshToken): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()
|
||||
->parse(
|
||||
$refreshToken,
|
||||
new SignedWith(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey()
|
||||
),
|
||||
new StrictValidAt(
|
||||
$this->clock,
|
||||
$this->clock->now()->diff($this->getRefreshExpireAt($this->clock->now()))
|
||||
),
|
||||
$this->getBlackListConstraint(),
|
||||
$this->accessTokenConstraint
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function addBlackList(UnencryptedToken $token): bool
|
||||
{
|
||||
return $this->getCacheDriver()->set($token->toString(), 1, $this->getBlackConfig('ttl', 600));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function hasBlackList(UnencryptedToken $token): bool
|
||||
{
|
||||
return $this->getCacheDriver()->has($token->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function removeBlackList(UnencryptedToken $token): bool
|
||||
{
|
||||
return $this->getCacheDriver()->delete($token->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return Arr::get($this->config, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JwtFacade
|
||||
*/
|
||||
private function getJwtFacade(): JwtFacade
|
||||
{
|
||||
return new JwtFacade(clock: $this->clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Signer
|
||||
*/
|
||||
private function getSigner(): Signer
|
||||
{
|
||||
return Arr::get($this->config, 'alg');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Key
|
||||
*/
|
||||
private function getSigningKey(): Key
|
||||
{
|
||||
return Arr::get($this->config, 'key');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DriverInterface
|
||||
*/
|
||||
private function getCacheDriver(): DriverInterface
|
||||
{
|
||||
return $this->cacheManager->getDriver($this->getBlackConfig('connection'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
private function getBlackConfig(string $name, mixed $default = null): mixed
|
||||
{
|
||||
return Arr::get($this->config, 'blacklist.' . $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Constraint
|
||||
*/
|
||||
private function getBlackListConstraint(): Constraint
|
||||
{
|
||||
return new BlackListConstraint((bool) $this->getBlackConfig('enable', false), $this->getCacheDriver());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable $immutable
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
private function getExpireAt(\DateTimeImmutable $immutable): \DateTimeImmutable
|
||||
{
|
||||
return Carbon::create($immutable)
|
||||
->addSeconds(Arr::get($this->config, 'ttl', 3600))
|
||||
->toDateTimeImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable $immutable
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
private function getRefreshExpireAt(\DateTimeImmutable $immutable): \DateTimeImmutable
|
||||
{
|
||||
return Carbon::create($immutable)
|
||||
->addSeconds(Arr::get($this->config, 'refresh_ttl', 7200))
|
||||
->toDateTimeImmutable();
|
||||
}
|
||||
}
|
||||
16
app/Lib/Jwt/AccessTokenConstraint.php
Normal file
16
app/Lib/Jwt/AccessTokenConstraint.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
class AccessTokenConstraint implements Constraint
|
||||
{
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token->isRelatedTo('refresh')) throw ConstraintViolation::error('Token is not a refresh token', $this);
|
||||
}
|
||||
}
|
||||
33
app/Lib/Jwt/BlackListConstraint.php
Normal file
33
app/Lib/Jwt/BlackListConstraint.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Hyperf\Cache\Driver\DriverInterface;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
class BlackListConstraint implements Constraint
|
||||
{
|
||||
/**
|
||||
* @param bool $enable
|
||||
* @param DriverInterface $cache
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly bool $enable,
|
||||
private readonly DriverInterface $cache
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param Token $token
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if ($this->enable !== true) return;
|
||||
|
||||
if ($this->cache->has($token->toString())) throw ConstraintViolation::error('Token is in blacklist', $this);
|
||||
}
|
||||
}
|
||||
15
app/Lib/Jwt/Clock.php
Normal file
15
app/Lib/Jwt/Clock.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
class Clock implements ClockInterface
|
||||
{
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return Carbon::now()->toDateTimeImmutable();
|
||||
}
|
||||
}
|
||||
7
app/Lib/Jwt/Jwt.php
Normal file
7
app/Lib/Jwt/Jwt.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use App\Interface\JwtInterface;
|
||||
|
||||
final class Jwt extends AbstractJwt implements JwtInterface {}
|
||||
51
app/Lib/Jwt/JwtFactory.php
Normal file
51
app/Lib/Jwt/JwtFactory.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use App\Interface\JwtInterface;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
final class JwtFactory
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ConfigInterface $config,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return JwtInterface
|
||||
*/
|
||||
public function get(string $name = 'default'): JwtInterface
|
||||
{
|
||||
return make(Jwt::class, [
|
||||
'config' => $this->getConfig($name),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景配置
|
||||
* @param string $scene
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig(string $scene): array
|
||||
{
|
||||
if ($scene === 'default') {
|
||||
return $this->config->get($this->getConfigKey());
|
||||
}
|
||||
return Arr::merge(
|
||||
$this->config->get($this->getConfigKey()),
|
||||
$this->config->get($this->getConfigKey($scene), [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
private function getConfigKey(string $name = 'default'): string
|
||||
{
|
||||
return 'jwt.' . $name;
|
||||
}
|
||||
}
|
||||
15
app/Lib/Jwt/RefreshTokenConstraint.php
Normal file
15
app/Lib/Jwt/RefreshTokenConstraint.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
class RefreshTokenConstraint implements Constraint
|
||||
{
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if ($token->isRelatedTo('refresh')) throw ConstraintViolation::error('Token is a refresh token', $this);
|
||||
}
|
||||
}
|
||||
16
app/Lib/Return/AdminReturn.php
Normal file
16
app/Lib/Return/AdminReturn.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Return;
|
||||
|
||||
class AdminReturn extends CommonReturn
|
||||
{
|
||||
/**
|
||||
* 通用返回
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
final protected function afterSuccess(array $res): array
|
||||
{
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
16
app/Lib/Return/ApiReturn.php
Normal file
16
app/Lib/Return/ApiReturn.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Return;
|
||||
|
||||
class ApiReturn extends CommonReturn
|
||||
{
|
||||
/**
|
||||
* 通用返回
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
final protected function afterSuccess(array $res): array
|
||||
{
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
53
app/Lib/Return/CommonReturn.php
Normal file
53
app/Lib/Return/CommonReturn.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Return;
|
||||
|
||||
use App\Constants\ResultCode;
|
||||
|
||||
abstract class CommonReturn
|
||||
{
|
||||
/**
|
||||
* 通用 success 返回
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int|ResultCode $code
|
||||
* @param array $debug
|
||||
* @return array
|
||||
*/
|
||||
final public function success(string $msg = 'success', array $data = [], ResultCode|int $code = ResultCode::SUCCESS, array $debug = []): array
|
||||
{
|
||||
$res = [
|
||||
'code' => $code,
|
||||
'message' => $msg,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
return $this->afterSuccess(array_merge($res, $debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用 fail 返回
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int|ResultCode $code
|
||||
* @param array $debug
|
||||
* @return array
|
||||
*/
|
||||
final public function error(string $msg = 'failed', ResultCode|int $code = ResultCode::ERROR, array $data = [], array $debug = []): array
|
||||
{
|
||||
$res = [
|
||||
'code' => $code,
|
||||
'message' => $msg,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
return $this->afterSuccess(array_merge($res, $debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用类调子类返回方便切面类识别
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function afterSuccess(array $res): array;
|
||||
}
|
||||
66
app/Listener/DbQueryExecutedListener.php
Normal file
66
app/Listener/DbQueryExecutedListener.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Listener;
|
||||
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Database\Events\QueryExecuted;
|
||||
use Hyperf\Event\Annotation\Listener;
|
||||
use Hyperf\Event\Contract\ListenerInterface;
|
||||
use Hyperf\Logger\LoggerFactory;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
#[Listener]
|
||||
class DbQueryExecutedListener implements ListenerInterface
|
||||
{
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->logger = $container->get(LoggerFactory::class)->get('sql');
|
||||
}
|
||||
|
||||
public function listen(): array
|
||||
{
|
||||
return [
|
||||
QueryExecuted::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryExecuted $event
|
||||
*/
|
||||
public function process(object $event): void
|
||||
{
|
||||
if ($event instanceof QueryExecuted) {
|
||||
$sql = $event->sql;
|
||||
if (! Arr::isAssoc($event->bindings)) {
|
||||
$position = 0;
|
||||
foreach ($event->bindings as $value) {
|
||||
$position = strpos($sql, '?', $position);
|
||||
if ($position === false) {
|
||||
break;
|
||||
}
|
||||
$value = "'{$value}'";
|
||||
$sql = substr_replace($sql, $value, $position, 1);
|
||||
$position += strlen($value);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->info(sprintf('[%s] %s', $event->time, $sql));
|
||||
}
|
||||
}
|
||||
}
|
||||
74
app/Listener/QueueHandleListener.php
Normal file
74
app/Listener/QueueHandleListener.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Listener;
|
||||
|
||||
use Hyperf\AsyncQueue\AnnotationJob;
|
||||
use Hyperf\AsyncQueue\Event\AfterHandle;
|
||||
use Hyperf\AsyncQueue\Event\BeforeHandle;
|
||||
use Hyperf\AsyncQueue\Event\Event;
|
||||
use Hyperf\AsyncQueue\Event\FailedHandle;
|
||||
use Hyperf\AsyncQueue\Event\RetryHandle;
|
||||
use Hyperf\Event\Annotation\Listener;
|
||||
use Hyperf\Event\Contract\ListenerInterface;
|
||||
use Hyperf\Logger\LoggerFactory;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
#[Listener]
|
||||
class QueueHandleListener implements ListenerInterface
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->logger = $container->get(LoggerFactory::class)->get('queue');
|
||||
}
|
||||
|
||||
public function listen(): array
|
||||
{
|
||||
return [
|
||||
AfterHandle::class,
|
||||
BeforeHandle::class,
|
||||
FailedHandle::class,
|
||||
RetryHandle::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function process(object $event): void
|
||||
{
|
||||
if ($event instanceof Event && $event->getMessage()->job()) {
|
||||
$job = $event->getMessage()->job();
|
||||
$jobClass = get_class($job);
|
||||
if ($job instanceof AnnotationJob) {
|
||||
$jobClass = sprintf('Job[%s@%s]', $job->class, $job->method);
|
||||
}
|
||||
$date = date('Y-m-d H:i:s');
|
||||
|
||||
switch (true) {
|
||||
case $event instanceof BeforeHandle:
|
||||
$this->logger->info(sprintf('[%s] Processing %s.', $date, $jobClass));
|
||||
break;
|
||||
case $event instanceof AfterHandle:
|
||||
$this->logger->info(sprintf('[%s] Processed %s.', $date, $jobClass));
|
||||
break;
|
||||
case $event instanceof FailedHandle:
|
||||
$this->logger->error(sprintf('[%s] Failed %s.', $date, $jobClass));
|
||||
$this->logger->error((string) $event->getThrowable());
|
||||
break;
|
||||
case $event instanceof RetryHandle:
|
||||
$this->logger->warning(sprintf('[%s] Retried %s.', $date, $jobClass));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
app/Listener/ResumeExitCoordinatorListener.php
Normal file
35
app/Listener/ResumeExitCoordinatorListener.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\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();
|
||||
}
|
||||
}
|
||||
113
app/Model/AdminMenu.php
Normal file
113
app/Model/AdminMenu.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
use App\Constants\Model\AdminUser\AdminMenuStatusCode;
|
||||
use App\Model\Meta\AdminUserMeta;
|
||||
use App\Model\Meta\MetaCast;
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Database\Model\Events\Deleting;
|
||||
use Hyperf\Database\Model\Relations\BelongsToMany;
|
||||
use Hyperf\Database\Model\Relations\HasMany;
|
||||
use Hyperf\Database\Model\Collection;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $parent_id
|
||||
* @property string $name
|
||||
* @property AdminUserMeta $meta
|
||||
* @property string $path
|
||||
* @property string $component
|
||||
* @property string $redirect
|
||||
* @property int $status
|
||||
* @property int $sort
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
* @property string $remark
|
||||
* @property Collection|AdminRole[] $roles
|
||||
* @property Collection|AdminMenu[] $children
|
||||
*/
|
||||
class AdminMenu extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_menu';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [
|
||||
'id',
|
||||
'parent_id',
|
||||
'name',
|
||||
'component',
|
||||
'redirect',
|
||||
'status',
|
||||
'sort',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'remark',
|
||||
'meta',
|
||||
'path',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'parent_id' => 'integer',
|
||||
'status' => AdminMenuStatusCode::class,
|
||||
'sort' => 'integer',
|
||||
'created_by' => 'integer',
|
||||
'updated_by' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'meta' => MetaCast::class,
|
||||
'path' => 'string',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 通过中间表获取角色.
|
||||
*/
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminRole::class,
|
||||
'admin_role_belongs_menu',
|
||||
'menu_id',
|
||||
'role_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany
|
||||
*/
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this
|
||||
->hasMany(self::class, 'parent_id', 'id')
|
||||
->where('status', AdminMenuStatusCode::Normal)
|
||||
->orderBy('sort')
|
||||
->with('children');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Deleting $event
|
||||
* @return void
|
||||
*/
|
||||
public function deleting(Deleting $event): void
|
||||
{
|
||||
$this->roles()->detach();
|
||||
}
|
||||
}
|
||||
96
app/Model/AdminRole.php
Normal file
96
app/Model/AdminRole.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
use Hyperf\Database\Model\Events\Deleting;
|
||||
use Hyperf\Database\Model\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $code
|
||||
* @property int $status
|
||||
* @property int $sort
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property string $remark
|
||||
*/
|
||||
class AdminRole extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_role';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [
|
||||
'id',
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
'sort',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'remark'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'status' => 'integer',
|
||||
'sort' => 'integer',
|
||||
'created_by' => 'integer',
|
||||
'updated_by' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function adminMenus(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminMenu::class,
|
||||
'role_belongs_menu',
|
||||
'role_id',
|
||||
'menu_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function adminUsers(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminUser::class,
|
||||
'admin_user_belongs_role',
|
||||
'role_id',
|
||||
'user_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Deleting $event
|
||||
* @return void
|
||||
*/
|
||||
public function deleting(Deleting $event): void
|
||||
{
|
||||
$this->adminUsers()->detach();
|
||||
$this->adminMenus()->detach();
|
||||
}
|
||||
}
|
||||
32
app/Model/AdminRoleBelongsMenu.php
Normal file
32
app/Model/AdminRoleBelongsMenu.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $admin_role_id
|
||||
* @property int $admin_menu_id
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
*/
|
||||
class AdminRoleBelongsMenu extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_role_belongs_menu';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['id' => 'integer', 'admin_role_id' => 'integer', 'admin_menu_id' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime'];
|
||||
}
|
||||
189
app/Model/AdminUser.php
Normal file
189
app/Model/AdminUser.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
use App\Constants\Model\AdminUser\AdminUserStatusCode;
|
||||
use App\Constants\Model\AdminUser\AdminUserTypeCode;
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Collection\Enumerable;
|
||||
use Hyperf\Database\Model\Collection;
|
||||
use Hyperf\Database\Model\Events\Creating;
|
||||
use Hyperf\Database\Model\Events\Deleted;
|
||||
use Hyperf\Database\Model\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $username
|
||||
* @property string $password
|
||||
* @property string $user_type
|
||||
* @property string $nickname
|
||||
* @property string $phone
|
||||
* @property string $email
|
||||
* @property string $avatar
|
||||
* @property string $signed
|
||||
* @property AdminUserStatusCode $status
|
||||
* @property string $login_ip
|
||||
* @property string $login_time
|
||||
* @property string $backend_setting
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
* @property string $remark
|
||||
*/
|
||||
class AdminUser extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_user';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [
|
||||
'id',
|
||||
'username',
|
||||
'password',
|
||||
'user_type',
|
||||
'nickname',
|
||||
'phone',
|
||||
'email',
|
||||
'avatar',
|
||||
'signed',
|
||||
'status',
|
||||
'login_ip',
|
||||
'login_time',
|
||||
'backend_setting',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'remark'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = [
|
||||
'id' => 'integer',
|
||||
'status' => AdminUserStatusCode::class,
|
||||
'user_type' => AdminUserTypeCode::class,
|
||||
'created_by' => 'integer',
|
||||
'updated_by' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'backend_setting' => 'json',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* 隐藏的字段列表.
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $hidden = ['password'];
|
||||
|
||||
|
||||
/**
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminRole::class,
|
||||
'admin_user_belongs_role',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Creating $event
|
||||
* @return void
|
||||
*/
|
||||
public function creating(Creating $event): void
|
||||
{
|
||||
if (!$this->isDirty('password')) $this->resetPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Deleted $event
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(Deleted $event): void
|
||||
{
|
||||
$this->roles()->detach();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function resetPassword(): void
|
||||
{
|
||||
$this->password = 'admin';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @return void
|
||||
*/
|
||||
public function setPasswordAttribute(string $password): void
|
||||
{
|
||||
$this->attributes['password'] = password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyPassword(string $password): bool
|
||||
{
|
||||
return password_verify($password, $this->password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuperAdmin(): bool
|
||||
{
|
||||
return $this->roles()->where('code','SuperAdmin')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
* @return Collection
|
||||
*/
|
||||
public function getRoles(array $fields): Collection
|
||||
{
|
||||
return $this->roles()
|
||||
->where('status',AdminUserStatusCode::Normal)
|
||||
->select($fields)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Hyperf\Collection\Collection|Enumerable|Collection<int, AdminMenu>
|
||||
*/
|
||||
public function getPermissions(): Collection|Enumerable|\Hyperf\Collection\Collection
|
||||
{
|
||||
return $this->roles()
|
||||
->with('adminMenus')
|
||||
->orderBy('sort')
|
||||
->get()
|
||||
->pluck('adminMenus')
|
||||
->flatten();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPermission(string $permission): bool
|
||||
{
|
||||
return $this->roles()
|
||||
->whereRelation('adminMenus','name',$permission)
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
27
app/Model/AdminUserBelongsMenu.php
Normal file
27
app/Model/AdminUserBelongsMenu.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
class AdminUserBelongsMenu extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_user_belongs_menu';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = [];
|
||||
}
|
||||
32
app/Model/AdminUserBelongsRole.php
Normal file
32
app/Model/AdminUserBelongsRole.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $admin_user_id
|
||||
* @property int $admin_role_id
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
*/
|
||||
class AdminUserBelongsRole extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_user_belongs_role';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['id' => 'integer', 'admin_user_id' => 'integer', 'admin_role_id' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime'];
|
||||
}
|
||||
37
app/Model/AdminUserLoginLog.php
Normal file
37
app/Model/AdminUserLoginLog.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $admin_user_id
|
||||
* @property string $username
|
||||
* @property string $ip
|
||||
* @property string $os
|
||||
* @property string $browser
|
||||
* @property int $status
|
||||
* @property string $message
|
||||
* @property string $login_time
|
||||
* @property string $remark
|
||||
*/
|
||||
class AdminUserLoginLog extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_user_login_log';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['id' => 'integer', 'admin_user_id' => 'integer', 'status' => 'integer'];
|
||||
}
|
||||
37
app/Model/AdminUserOperationLog.php
Normal file
37
app/Model/AdminUserOperationLog.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $admin_user_id
|
||||
* @property string $username
|
||||
* @property string $method
|
||||
* @property string $router
|
||||
* @property string $service_name
|
||||
* @property string $ip
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property string $remark
|
||||
*/
|
||||
class AdminUserOperationLog extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*/
|
||||
protected ?string $table = 'admin_user_operation_log';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected array $fillable = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*/
|
||||
protected array $casts = ['id' => 'integer', 'admin_user_id' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime'];
|
||||
}
|
||||
78
app/Model/Meta/AdminUserMeta.php
Normal file
78
app/Model/Meta/AdminUserMeta.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model\Meta;
|
||||
|
||||
use App\Model\Model;
|
||||
|
||||
/**
|
||||
* @property string $title 标题
|
||||
* @property string $i18n 国际化
|
||||
* @property string $badge 徽章
|
||||
* @property string $icon 图标
|
||||
* @property bool $affix 是否固定
|
||||
* @property bool $hidden 是否隐藏
|
||||
* @property string $type 类型
|
||||
* @property bool $cache 是否缓存
|
||||
* @property bool $copyright 是否显示版权
|
||||
* @property string $link 链接
|
||||
* @property string $componentPath 视图文件类型
|
||||
* @property string $componentSuffix 视图前缀路径
|
||||
* @property string $breadcrumbEnable 是否显示面包屑
|
||||
* @property string $activeName 激活高亮的菜单标识
|
||||
* @property string $auth 前端权限判断,允许访问的权限码
|
||||
* @property string $role 前端权限判断,允许访问的角色码
|
||||
* @property string $user 前端权限判断,允许访问的用户名
|
||||
*/
|
||||
final class AdminUserMeta extends Model
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public bool $incrementing = false;
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $fillable = [
|
||||
'title',
|
||||
'i18n',
|
||||
'badge',
|
||||
'icon',
|
||||
'affix',
|
||||
'hidden',
|
||||
'type',
|
||||
'cache',
|
||||
'copyright',
|
||||
'breadcrumbEnable',
|
||||
'componentPath',
|
||||
'componentSuffix',
|
||||
'link',
|
||||
'activeName',
|
||||
'auth',
|
||||
'role',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $casts = [
|
||||
'affix' => 'boolean',
|
||||
'hidden' => 'boolean',
|
||||
'cache' => 'boolean',
|
||||
'copyright' => 'boolean',
|
||||
'breadcrumbEnable' => 'boolean',
|
||||
'title' => 'string',
|
||||
'componentPath' => 'string',
|
||||
'componentSuffix' => 'string',
|
||||
'i18n' => 'string',
|
||||
'badge' => 'string',
|
||||
'icon' => 'string',
|
||||
'type' => 'string',
|
||||
'link' => 'string',
|
||||
'activeName' => 'string',
|
||||
'auth' => 'array',
|
||||
'role' => 'array',
|
||||
'user' => 'array',
|
||||
];
|
||||
}
|
||||
33
app/Model/Meta/MetaCast.php
Normal file
33
app/Model/Meta/MetaCast.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model\Meta;
|
||||
|
||||
use Hyperf\Codec\Json;
|
||||
use Hyperf\Contract\CastsAttributes;
|
||||
|
||||
class MetaCast implements CastsAttributes
|
||||
{
|
||||
/**
|
||||
* @param $model
|
||||
* @param string $key
|
||||
* @param $value
|
||||
* @param array $attributes
|
||||
* @return AdminUserMeta
|
||||
*/
|
||||
public function get($model, string $key, $value, array $attributes): AdminUserMeta
|
||||
{
|
||||
return new AdminUserMeta(empty($value) ? [] : Json::decode($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $model
|
||||
* @param string $key
|
||||
* @param $value
|
||||
* @param array $attributes
|
||||
* @return array|string
|
||||
*/
|
||||
public function set($model, string $key, $value, array $attributes): array|string
|
||||
{
|
||||
return Json::encode($value);
|
||||
}
|
||||
}
|
||||
22
app/Model/Model.php
Normal file
22
app/Model/Model.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use Hyperf\DbConnection\Model\Model as BaseModel;
|
||||
use Hyperf\ModelCache\Cacheable;
|
||||
use Hyperf\ModelCache\CacheableInterface;
|
||||
|
||||
abstract class Model extends BaseModel implements CacheableInterface
|
||||
{
|
||||
use Cacheable;
|
||||
}
|
||||
21
app/Process/AsyncQueueConsumer.php
Normal file
21
app/Process/AsyncQueueConsumer.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace App\Process;
|
||||
|
||||
use Hyperf\AsyncQueue\Process\ConsumerProcess;
|
||||
use Hyperf\Process\Annotation\Process;
|
||||
|
||||
#[Process]
|
||||
class AsyncQueueConsumer extends ConsumerProcess
|
||||
{
|
||||
}
|
||||
34
app/Repository/AdminUserRepository.php
Normal file
34
app/Repository/AdminUserRepository.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Model\AdminUser;
|
||||
|
||||
/**
|
||||
* Class AdminUserRepository
|
||||
* @extends BaseRepository<AdminUser>
|
||||
*/
|
||||
final class AdminUserRepository extends BaseRepository
|
||||
{
|
||||
public function __construct(protected readonly AdminUser $model) {}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @return AdminUser|null
|
||||
*/
|
||||
public function findByUserName(string $username): AdminUser|null
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->model->newQuery()
|
||||
->where('username', $username)
|
||||
->first();
|
||||
}
|
||||
}
|
||||
151
app/Repository/BaseRepository.php
Normal file
151
app/Repository/BaseRepository.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Repository\Traits\BootTrait;
|
||||
use App\Repository\Traits\RepositoryOrderByTrait;
|
||||
use Hyperf\Collection\Collection;
|
||||
use Hyperf\Contract\LengthAwarePaginatorInterface;
|
||||
use Hyperf\Database\Model\Builder;
|
||||
use Hyperf\Database\Model\Model;
|
||||
use Hyperf\DbConnection\Traits\HasContainer;
|
||||
use Hyperf\Paginator\AbstractPaginator;
|
||||
|
||||
/**
|
||||
* @template T of Model
|
||||
* @property T $model
|
||||
*/
|
||||
abstract class BaseRepository
|
||||
{
|
||||
use BootTrait;
|
||||
use HasContainer;
|
||||
use RepositoryOrderByTrait;
|
||||
|
||||
public const string PER_PAGE_PARAM_NAME = 'per_page';
|
||||
public function handleSearch(Builder $query, array $params): Builder
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function handleItems(Collection $items): Collection
|
||||
{
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function handlePage(LengthAwarePaginatorInterface $paginator): array
|
||||
{
|
||||
if ($paginator instanceof AbstractPaginator) {
|
||||
$items = $paginator->getCollection();
|
||||
} else {
|
||||
$items = Collection::make($paginator->items());
|
||||
}
|
||||
$items = $this->handleItems($items);
|
||||
return [
|
||||
'list' => $items->toArray(),
|
||||
'total' => $paginator->total(),
|
||||
];
|
||||
}
|
||||
|
||||
public function list(array $params = []): Collection
|
||||
{
|
||||
return $this->handleItems($this->perQuery($this->getQuery(), $params)->get());
|
||||
}
|
||||
|
||||
public function count(array $params = []): int
|
||||
{
|
||||
return $this->perQuery($this->getQuery(), $params)->count();
|
||||
}
|
||||
|
||||
public function page(array $params = [], ?int $page = null, ?int $pageSize = null): array
|
||||
{
|
||||
$result = $this->perQuery($this->getQuery(), $params)->paginate(
|
||||
perPage: $pageSize,
|
||||
pageName: static::PER_PAGE_PARAM_NAME,
|
||||
page: $page,
|
||||
);
|
||||
return $this->handlePage($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return T
|
||||
*/
|
||||
public function create(array $data): mixed
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->getQuery()->create($data);
|
||||
}
|
||||
|
||||
public function updateById(mixed $id, array $data): bool
|
||||
{
|
||||
return (bool) $this->getQuery()->whereKey($id)->first()?->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|T
|
||||
*/
|
||||
public function saveById(mixed $id, array $data): mixed
|
||||
{
|
||||
$model = $this->getQuery()->whereKey($id)->first();
|
||||
if ($model) {
|
||||
$model->fill($data)->save();
|
||||
return $model;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function deleteById(mixed $id): int
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->model::destroy($id);
|
||||
}
|
||||
|
||||
public function forceDeleteById(mixed $id): bool
|
||||
{
|
||||
return (bool) $this->getQuery()->whereKey($id)->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|T
|
||||
*/
|
||||
public function findById(mixed $id): mixed
|
||||
{
|
||||
return $this->getQuery()->whereKey($id)->first();
|
||||
}
|
||||
|
||||
public function findByField(mixed $id, string $field): mixed
|
||||
{
|
||||
return $this->getQuery()->whereKey($id)->value($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|T
|
||||
*/
|
||||
public function findByFilter(array $params): mixed
|
||||
{
|
||||
return $this->perQuery($this->getQuery(), $params)->first();
|
||||
}
|
||||
|
||||
public function perQuery(Builder $query, array $params): Builder
|
||||
{
|
||||
$this->startBoot($query, $params);
|
||||
return $this->handleSearch($query, $params);
|
||||
}
|
||||
|
||||
public function getQuery(): Builder
|
||||
{
|
||||
return $this->model->newQuery();
|
||||
}
|
||||
|
||||
public function existsById(mixed $id): bool
|
||||
{
|
||||
return (bool) $this->getQuery()->whereKey($id)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return T
|
||||
*/
|
||||
public function getModel(): Model
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
}
|
||||
20
app/Repository/Traits/BootTrait.php
Normal file
20
app/Repository/Traits/BootTrait.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository\Traits;
|
||||
|
||||
use function Hyperf\Support\class_basename;
|
||||
use function Hyperf\Support\class_uses_recursive;
|
||||
|
||||
trait BootTrait
|
||||
{
|
||||
protected function startBoot(...$params): void
|
||||
{
|
||||
$traits = class_uses_recursive(static::class);
|
||||
foreach ($traits as $trait) {
|
||||
$method = 'boot' . class_basename($trait);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->{$method}(...$params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
app/Repository/Traits/RepositoryOrderByTrait.php
Normal file
38
app/Repository/Traits/RepositoryOrderByTrait.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository\Traits;
|
||||
|
||||
use Hyperf\Database\Model\Builder;
|
||||
|
||||
trait RepositoryOrderByTrait
|
||||
{
|
||||
public function handleOrderBy(Builder $query, $params): Builder
|
||||
{
|
||||
if ($this->enablePageOrderBy()) {
|
||||
$orderByField = $params[$this->getOrderByParamName()] ?? $query->getModel()->getKeyName();
|
||||
$orderByDirection = $params[$this->getOrderByDirectionParamName()] ?? 'desc';
|
||||
$query->orderBy($orderByField, $orderByDirection);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function bootRepositoryOrderByTrait(Builder $query, array $params): void
|
||||
{
|
||||
$this->handleOrderBy($query, $params);
|
||||
}
|
||||
|
||||
protected function getOrderByParamName(): string
|
||||
{
|
||||
return 'order_by';
|
||||
}
|
||||
|
||||
protected function getOrderByDirectionParamName(): string
|
||||
{
|
||||
return 'order_by_direction';
|
||||
}
|
||||
|
||||
protected function enablePageOrderBy(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
28
app/Request/Admin/LoginRequest.php
Normal file
28
app/Request/Admin/LoginRequest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Request\Admin;
|
||||
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
|
||||
class LoginRequest 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 [
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
52
app/Service/Admin/BaseAdminService.php
Normal file
52
app/Service/Admin/BaseAdminService.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service\Admin;
|
||||
|
||||
use App\Lib\Return\AdminReturn;
|
||||
use Hyperf\Context\Context;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
|
||||
abstract class BaseAdminService
|
||||
{
|
||||
/**
|
||||
* 请求对象注入
|
||||
* @var RequestInterface
|
||||
*/
|
||||
#[Inject]
|
||||
protected RequestInterface $request;
|
||||
|
||||
/**
|
||||
* 返回对象注入
|
||||
* @var AdminReturn
|
||||
*/
|
||||
#[Inject]
|
||||
protected AdminReturn $adminReturn;
|
||||
|
||||
/**
|
||||
* 管理员 id
|
||||
* @var int
|
||||
*/
|
||||
protected int $adminId = 0;
|
||||
|
||||
/**
|
||||
* 主构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->adminId = Context::get('admin_id',0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数抽象类
|
||||
*/
|
||||
abstract public function handle();
|
||||
}
|
||||
72
app/Service/Admin/Login/LoginService.php
Normal file
72
app/Service/Admin/Login/LoginService.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service\Admin\Login;
|
||||
|
||||
use App\Constants\Model\AdminUser\AdminUserStatusCode;
|
||||
use App\Exception\ErrException;
|
||||
use App\Interface\JwtInterface;
|
||||
use App\Lib\Jwt\JwtFactory;
|
||||
use App\Repository\AdminUserRepository;
|
||||
use App\Service\Admin\BaseAdminService;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
class LoginService extends BaseAdminService
|
||||
{
|
||||
/**
|
||||
* @var string jwt场景
|
||||
*/
|
||||
private string $jwt = 'admin';
|
||||
|
||||
/**
|
||||
* @var AdminUserRepository
|
||||
*/
|
||||
#[Inject]
|
||||
protected AdminUserRepository $userRepository;
|
||||
|
||||
/**
|
||||
* @var JwtFactory
|
||||
*/
|
||||
#[Inject]
|
||||
protected JwtFactory $jwtFactory;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function handle(): array
|
||||
{
|
||||
$adminInfo = $this->userRepository->findByUserName((string)$this->request->input('username'));
|
||||
|
||||
if (!$adminInfo) throw new ErrException('后台管理员不存在');
|
||||
|
||||
if (! $adminInfo->verifyPassword((string) $this->request->input('password'))) {
|
||||
// $this->dispatcher->dispatch(new UserLoginEvent($user, $ip, $os, $browser, false));
|
||||
throw new ErrException('密码错误');
|
||||
}
|
||||
|
||||
if ($adminInfo->status == AdminUserStatusCode::DISABLE) throw new ErrException('用户已禁用');
|
||||
|
||||
$jwtHandle = $this->getJwt();
|
||||
|
||||
return [
|
||||
'access_token' => $jwtHandle->builderAccessToken((string) $adminInfo->id)->toString(),
|
||||
'refresh_token' => $jwtHandle->builderRefreshToken((string) $adminInfo->id)->toString(),
|
||||
'expire_at' => (int) $jwtHandle->getConfig('ttl', 0),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JwtInterface
|
||||
*/
|
||||
private function getJwt(): JwtInterface
|
||||
{
|
||||
return $this->jwtFactory->get($this->jwt);
|
||||
}
|
||||
}
|
||||
25
bin/hyperf.php
Normal file
25
bin/hyperf.php
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
ini_set('display_errors', 'on');
|
||||
ini_set('display_startup_errors', 'on');
|
||||
ini_set('memory_limit', '1G');
|
||||
|
||||
error_reporting(E_ALL);
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
|
||||
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
|
||||
|
||||
require BASE_PATH . '/vendor/autoload.php';
|
||||
|
||||
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', Hyperf\Engine\DefaultOption::hookFlags());
|
||||
|
||||
// Self-called anonymous function that creates its own scope and keep the global namespace clean.
|
||||
(function () {
|
||||
Hyperf\Di\ClassLoader::init();
|
||||
/** @var Psr\Container\ContainerInterface $container */
|
||||
$container = require BASE_PATH . '/config/container.php';
|
||||
|
||||
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
|
||||
$application->run();
|
||||
})();
|
||||
85
composer.json
Normal file
85
composer.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "hyperf/hyperf-skeleton",
|
||||
"type": "project",
|
||||
"keywords": [
|
||||
"php",
|
||||
"swoole",
|
||||
"framework",
|
||||
"hyperf",
|
||||
"microservice",
|
||||
"middleware"
|
||||
],
|
||||
"description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.",
|
||||
"license": "Apache-2.0",
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"hyperf/amqp": "~3.1.0",
|
||||
"hyperf/async-queue": "~3.1.0",
|
||||
"hyperf/cache": "~3.1.0",
|
||||
"hyperf/command": "~3.1.0",
|
||||
"hyperf/config": "~3.1.0",
|
||||
"hyperf/constants": "~3.1.0",
|
||||
"hyperf/database": "~3.1.0",
|
||||
"hyperf/db-connection": "~3.1.0",
|
||||
"hyperf/engine": "^2.10",
|
||||
"hyperf/framework": "~3.1.0",
|
||||
"hyperf/guzzle": "~3.1.0",
|
||||
"hyperf/http-server": "~3.1.0",
|
||||
"hyperf/logger": "~3.1.0",
|
||||
"hyperf/memory": "~3.1.0",
|
||||
"hyperf/model-cache": "~3.1.0",
|
||||
"hyperf/paginator": "^3.1",
|
||||
"hyperf/process": "~3.1.0",
|
||||
"hyperf/redis": "~3.1.0",
|
||||
"hyperf/validation": "^3.1",
|
||||
"lcobucci/jwt": "^5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"hyperf/devtool": "~3.1.0",
|
||||
"hyperf/testing": "~3.1.0",
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"swoole/ide-helper": "^5.0"
|
||||
},
|
||||
"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/"
|
||||
},
|
||||
"files": []
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"HyperfTest\\": "./test/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": 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 --colors=always",
|
||||
"cs-fix": "php-cs-fixer fix $1",
|
||||
"analyse": "phpstan analyse --memory-limit 300M",
|
||||
"start": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"php ./bin/hyperf.php start"
|
||||
]
|
||||
}
|
||||
}
|
||||
10324
composer.lock
generated
Normal file
10324
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
config/autoload/amqp.php
Normal file
42
config/autoload/amqp.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'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,
|
||||
],
|
||||
],
|
||||
];
|
||||
21
config/autoload/annotations.php
Normal file
21
config/autoload/annotations.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
'scan' => [
|
||||
'paths' => [
|
||||
BASE_PATH . '/app',
|
||||
],
|
||||
'ignore_annotations' => [
|
||||
'mixin',
|
||||
],
|
||||
],
|
||||
];
|
||||
13
config/autoload/aspects.php
Normal file
13
config/autoload/aspects.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
];
|
||||
28
config/autoload/async_queue.php
Normal file
28
config/autoload/async_queue.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
'default' => [
|
||||
'driver' => \Hyperf\AsyncQueue\Driver\RedisDriver::class,
|
||||
'redis' => [
|
||||
'pool' => 'default',
|
||||
],
|
||||
'channel' => '{queue}',
|
||||
'timeout' => 2,
|
||||
'retry_seconds' => 5,
|
||||
'handle_timeout' => 10,
|
||||
'processes' => 1,
|
||||
'concurrent' => [
|
||||
'limit' => 10,
|
||||
],
|
||||
'max_messages' => 0,
|
||||
],
|
||||
];
|
||||
19
config/autoload/cache.php
Normal file
19
config/autoload/cache.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
'default' => [
|
||||
'driver' => Hyperf\Cache\Driver\RedisDriver::class,
|
||||
'packer' => Hyperf\Codec\Packer\PhpSerializerPacker::class,
|
||||
'prefix' => 'c:',
|
||||
'skip_cache_results' => [],
|
||||
],
|
||||
];
|
||||
13
config/autoload/commands.php
Normal file
13
config/autoload/commands.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
];
|
||||
51
config/autoload/databases.php
Normal file
51
config/autoload/databases.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'driver' => env('DB_DRIVER', 'mysql'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', 3306),
|
||||
'database' => env('DB_DATABASE', 'hyperf'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
'pool' => [
|
||||
'min_connections' => 1,
|
||||
'max_connections' => 10,
|
||||
'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' => '{mc:%s:m:%s}:%s:%s',
|
||||
'prefix' => 'default',
|
||||
'ttl' => 3600 * 24,
|
||||
'empty_model_ttl' => 600,
|
||||
'load_script' => true,
|
||||
],
|
||||
'commands' => [
|
||||
'gen:model' => [
|
||||
'path' => 'app/Model',
|
||||
'force_casts' => true,
|
||||
'inheritance' => 'Model',
|
||||
'uses' => '',
|
||||
'table_mapping' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
14
config/autoload/dependencies.php
Normal file
14
config/autoload/dependencies.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
Hyperf\Database\Schema\Blueprint::class => App\Common\Macros\BlueprintMacros::class,
|
||||
];
|
||||
53
config/autoload/devtool.php
Normal file
53
config/autoload/devtool.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
return [
|
||||
'generator' => [
|
||||
'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',
|
||||
],
|
||||
],
|
||||
/**
|
||||
* Supported IDEs: "sublime", "textmate", "cursor", "emacs", "macvim", "phpstorm", "idea",
|
||||
* "vscode", "vscode-insiders", "vscode-remote", "vscode-insiders-remote",
|
||||
* "atom", "nova", "netbeans", "xdebug"
|
||||
*/
|
||||
'ide' => env('DEVTOOL_IDE', ''),
|
||||
];
|
||||
21
config/autoload/exceptions.php
Normal file
21
config/autoload/exceptions.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
'handler' => [
|
||||
'http' => [
|
||||
Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
|
||||
App\Exception\Handler\ErrExceptionHandler::class,
|
||||
App\Exception\Handler\ValidationExceptionHandler::class,
|
||||
App\Exception\Handler\AppExceptionHandler::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
68
config/autoload/jwt.php
Normal file
68
config/autoload/jwt.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
use App\Lib\Jwt\Jwt;
|
||||
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
// jwt 配置 https://lcobucci-jwt.readthedocs.io/en/latest/
|
||||
'driver' => Jwt::class,
|
||||
// jwt 签名key
|
||||
'key' => InMemory::base64Encoded(env('JWT_SECRET')),
|
||||
// jwt 签名算法 可选 https://lcobucci-jwt.readthedocs.io/en/latest/supported-algorithms/
|
||||
'alg' => new Sha256(),
|
||||
// token过期时间,单位为秒
|
||||
'ttl' => (int) env('JWT_TTL', 3600),
|
||||
// 刷新token过期时间,单位为秒
|
||||
'refresh_ttl' => (int) env('JWT_REFRESH_TTL', 7200),
|
||||
// 黑名单模式
|
||||
'blacklist' => [
|
||||
// 是否开启黑名单
|
||||
'enable' => true,
|
||||
// 黑名单缓存前缀
|
||||
'prefix' => 'jwt_blacklist',
|
||||
// 黑名单缓存驱动
|
||||
'connection' => 'default',
|
||||
// 黑名单缓存时间 该时间一定要设置比token过期时间要大一点,最好设置跟过期时间一样
|
||||
'ttl' => (int) env('JWT_BLACKLIST_TTL', 7201),
|
||||
],
|
||||
'claims' => [
|
||||
// 默认的jwt claims
|
||||
RegisteredClaims::ISSUER => (string) env('APP_NAME'),
|
||||
],
|
||||
],
|
||||
'admin' => [
|
||||
// jwt 配置 https://lcobucci-jwt.readthedocs.io/en/latest/
|
||||
'driver' => Jwt::class,
|
||||
// jwt 签名key
|
||||
'key' => InMemory::base64Encoded(env('JWT_SECRET')),
|
||||
// jwt 签名算法 可选 https://lcobucci-jwt.readthedocs.io/en/latest/supported-algorithms/
|
||||
'alg' => new Sha256(),
|
||||
// token过期时间,单位为秒
|
||||
'ttl' => (int) env('ADMIN_JWT_TTL', 3600),
|
||||
// 刷新token过期时间,单位为秒
|
||||
'refresh_ttl' => (int) env('ADMIN_JWT_REFRESH_TTL', 7200),
|
||||
// 黑名单模式
|
||||
'blacklist' => [
|
||||
// 是否开启黑名单
|
||||
'enable' => true,
|
||||
// 黑名单缓存前缀
|
||||
'prefix' => 'jwt_blacklist',
|
||||
// 黑名单缓存驱动
|
||||
'connection' => 'default',
|
||||
// 黑名单缓存时间 该时间一定要设置比token过期时间要大一点,最好设置跟过期时间一样
|
||||
'ttl' => (int) env('ADMIN_JWT_BLACKLIST_TTL', 7201),
|
||||
],
|
||||
'claims' => [
|
||||
// 默认的jwt claims
|
||||
RegisteredClaims::ISSUER => 'ADMIN'. env('APP_NAME'),
|
||||
],
|
||||
],
|
||||
// 在你想要使用不同的场景时,可以在这里添加配置.可以填一个。其他会使用默认配置
|
||||
'application' => [
|
||||
// jwt 配置 https://lcobucci-jwt.readthedocs.io/en/latest/
|
||||
],
|
||||
];
|
||||
15
config/autoload/listeners.php
Normal file
15
config/autoload/listeners.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler::class,
|
||||
Hyperf\Command\Listener\FailToHandleListener::class,
|
||||
];
|
||||
30
config/autoload/logger.php
Normal file
30
config/autoload/logger.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
'default' => [
|
||||
'handler' => [
|
||||
'class' => Monolog\Handler\StreamHandler::class,
|
||||
'constructor' => [
|
||||
'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
|
||||
'level' => Monolog\Logger::DEBUG,
|
||||
],
|
||||
],
|
||||
'formatter' => [
|
||||
'class' => Monolog\Formatter\LineFormatter::class,
|
||||
'constructor' => [
|
||||
'format' => null,
|
||||
'dateFormat' => 'Y-m-d H:i:s',
|
||||
'allowInlineLineBreaks' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
19
config/autoload/middlewares.php
Normal file
19
config/autoload/middlewares.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
use Hyperf\Validation\Middleware\ValidationMiddleware;
|
||||
|
||||
return [
|
||||
'http' => [
|
||||
ValidationMiddleware::class,
|
||||
],
|
||||
];
|
||||
13
config/autoload/processes.php
Normal file
13
config/autoload/processes.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
];
|
||||
29
config/autoload/redis.php
Normal file
29
config/autoload/redis.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'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),
|
||||
],
|
||||
],
|
||||
];
|
||||
50
config/autoload/server.php
Normal file
50
config/autoload/server.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use Hyperf\Server\Event;
|
||||
use Hyperf\Server\Server;
|
||||
use Swoole\Constant;
|
||||
|
||||
return [
|
||||
'mode' => 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 => [Hyperf\HttpServer\Server::class, 'onRequest'],
|
||||
],
|
||||
'options' => [
|
||||
// Whether to enable request lifecycle event
|
||||
'enable_request_lifecycle' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'settings' => [
|
||||
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 => 2 * 1024 * 1024,
|
||||
Constant::OPTION_BUFFER_OUTPUT_SIZE => 2 * 1024 * 1024,
|
||||
],
|
||||
'callbacks' => [
|
||||
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'],
|
||||
],
|
||||
];
|
||||
16
config/autoload/translation.php
Normal file
16
config/autoload/translation.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
return [
|
||||
'locale' => 'zh_CN',
|
||||
'fallback_locale' => 'en',
|
||||
'path' => BASE_PATH . '/storage/languages',
|
||||
];
|
||||
33
config/config.php
Normal file
33
config/config.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use Hyperf\Contract\StdoutLoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
return [
|
||||
'app_name' => env('APP_NAME', 'skeleton'),
|
||||
'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,
|
||||
],
|
||||
],
|
||||
];
|
||||
21
config/container.php
Normal file
21
config/container.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* Initialize a dependency injection container that implemented PSR-11 and return the container.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Di\Container;
|
||||
use Hyperf\Di\Definition\DefinitionSourceFactory;
|
||||
|
||||
$container = new Container((new DefinitionSourceFactory())());
|
||||
|
||||
return ApplicationContext::setContainer($container);
|
||||
18
config/routes.php
Normal file
18
config/routes.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
use Hyperf\HttpServer\Router\Router;
|
||||
|
||||
Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController@index');
|
||||
|
||||
Router::get('/favicon.ico', function () {
|
||||
return '';
|
||||
});
|
||||
30
deploy.test.yml
Normal file
30
deploy.test.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
version: '3.7'
|
||||
services:
|
||||
hyperf:
|
||||
image: $REGISTRY_URL/$PROJECT_NAME:test
|
||||
environment:
|
||||
- "APP_PROJECT=hyperf"
|
||||
- "APP_ENV=testing"
|
||||
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
|
||||
18
docker-compose.yml
Normal file
18
docker-compose.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: '3'
|
||||
services:
|
||||
hyperf-skeleton:
|
||||
container_name: hyperf-skeleton
|
||||
image: hyperf-skeleton
|
||||
build:
|
||||
context: .
|
||||
volumes:
|
||||
- ./:/opt/www
|
||||
ports:
|
||||
- 9501:9501
|
||||
environment:
|
||||
- APP_ENV=dev
|
||||
- SCAN_CACHEABLE=false
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: hyperf-skeleton
|
||||
43
migrations/2025_09_11_174754_create_admin_user_table.php
Normal file
43
migrations/2025_09_11_174754_create_admin_user_table.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Hyperf\Database\Schema\Schema;
|
||||
use Hyperf\Database\Schema\Blueprint;
|
||||
use Hyperf\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('admin_user', function (Blueprint $table) {
|
||||
$table->comment('管理员信息表');
|
||||
$table->bigIncrements('id')->comment('用户ID,主键');
|
||||
$table->string('username', 20)->unique()->comment('用户名')->unique();
|
||||
$table->string('password', 100)->comment('密码');
|
||||
$table->string('user_type', 3)->default('100')->comment('用户类型:100=系统用户');
|
||||
$table->string('nickname', 30)->default('')->comment('用户昵称');
|
||||
$table->string('phone', 11)->default('')->comment('手机');
|
||||
$table->string('email', 50)->default('')->comment('用户邮箱');
|
||||
$table->string('avatar', 255)->default('')->comment('用户头像');
|
||||
$table->string('signed', 255)->default('')->comment('个人签名');
|
||||
$table->tinyInteger('status')->default(1)->comment('状态:1=正常,2=停用');
|
||||
$table->ipAddress('login_ip')->default('127.0.0.1')->comment('最后登陆IP');
|
||||
$table->timestamp('login_time')->useCurrent()->comment('最后登陆时间');
|
||||
$table->json('backend_setting')->nullable()->comment('后台设置数据');
|
||||
$table->unsignedBigInteger('created_by')->default(0)->comment('创建人ID');
|
||||
$table->unsignedBigInteger('updated_by')->default(0)->comment('更新人ID');
|
||||
$table->datetimes();
|
||||
$table->string('remark', 255)->default('')->comment('备注');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('admin_user');
|
||||
}
|
||||
};
|
||||
39
migrations/2025_09_11_175006_create_admin_menu_table.php
Normal file
39
migrations/2025_09_11_175006_create_admin_menu_table.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Hyperf\Database\Schema\Schema;
|
||||
use Hyperf\Database\Schema\Blueprint;
|
||||
use Hyperf\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('admin_menu', function (Blueprint $table) {
|
||||
$table->comment('后台菜单信息表');
|
||||
$table->bigIncrements('id')->comment('主键');
|
||||
$table->bigInteger('parent_id')->unsigned()->comment('父ID');
|
||||
$table->string('name', 50)->default('')->comment('菜单名称')->unique();
|
||||
$table->json('meta')->comment('附加属性')->nullable();
|
||||
$table->string('path', 60)->default('')->comment('路径');
|
||||
$table->string('component', 150)->default('')->comment('组件路径');
|
||||
$table->string('redirect', 100)->comment('重定向地址')->default('');
|
||||
$table->tinyInteger('status')->comment('状态:1=正常,2=停用')->default(1);
|
||||
$table->smallInteger('sort')->comment('排序')->default(0);
|
||||
$table->unsignedBigInteger('created_by')->default(0)->comment('创建人ID');
|
||||
$table->unsignedBigInteger('updated_by')->default(0)->comment('更新人ID');
|
||||
$table->datetimes();
|
||||
$table->string('remark', 60)->comment('备注')->default('');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('admin_menu');
|
||||
}
|
||||
};
|
||||
35
migrations/2025_09_11_175013_create_admin_role_table.php
Normal file
35
migrations/2025_09_11_175013_create_admin_role_table.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Hyperf\Database\Schema\Schema;
|
||||
use Hyperf\Database\Schema\Blueprint;
|
||||
use Hyperf\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('admin_role', function (Blueprint $table) {
|
||||
$table->comment('后台角色信息表');
|
||||
$table->bigIncrements('id')->comment('主键');
|
||||
$table->string('name', 30)->comment('角色名称');
|
||||
$table->string('code', 100)->comment('角色代码')->unique();
|
||||
$table->tinyInteger('status')->comment('状态:1=正常,2=停用')->default(1);
|
||||
$table->smallInteger('sort')->comment('排序')->default(0);
|
||||
$table->unsignedBigInteger('created_by')->default(0)->comment('创建人ID');
|
||||
$table->unsignedBigInteger('updated_by')->default(0)->comment('更新人ID');
|
||||
$table->datetimes();
|
||||
$table->string('remark')->comment('备注')->default('');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('admin_role');
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user