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.*
|
17
.env.example
Normal file
17
.env.example
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
APP_NAME=skeleton
|
||||||
|
APP_ENV=dev
|
||||||
|
|
||||||
|
DB_DRIVER=mysql
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=hyperf
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
DB_CHARSET=utf8mb4
|
||||||
|
DB_COLLATION=utf8mb4_unicode_ci
|
||||||
|
DB_PREFIX=
|
||||||
|
|
||||||
|
REDIS_HOST=localhost
|
||||||
|
REDIS_AUTH=(null)
|
||||||
|
REDIS_PORT=6379
|
||||||
|
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));
|
||||||
|
}
|
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.
|
63
README.md
Normal file
63
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.
|
24
app/Amqp/Consumer/AliSlsConsumer.php
Normal file
24
app/Amqp/Consumer/AliSlsConsumer.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Amqp\Consumer;
|
||||||
|
|
||||||
|
use App\Service\AliLogsSignService;
|
||||||
|
use Hyperf\Amqp\Result;
|
||||||
|
use Hyperf\Amqp\Annotation\Consumer;
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
|
||||||
|
#[Consumer(exchange: 'wh_service_ali_sls', routingKey: 'wh_service_ali_sls_key', queue: 'wh_service_ali_sls_queue', name: "AliSlsConsumer", nums: 5)]
|
||||||
|
class AliSlsConsumer extends BaseConsumer
|
||||||
|
{
|
||||||
|
#[Inject]
|
||||||
|
protected AliLogsSignService $aliLogsSignService;
|
||||||
|
|
||||||
|
public function handle($data): Result
|
||||||
|
{
|
||||||
|
$this->aliLogsSignService->putWebTracking($data);
|
||||||
|
|
||||||
|
return Result::ACK;
|
||||||
|
}
|
||||||
|
}
|
74
app/Amqp/Consumer/BaseConsumer.php
Normal file
74
app/Amqp/Consumer/BaseConsumer.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午6:37
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Amqp\Consumer;
|
||||||
|
|
||||||
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
||||||
|
use Hyperf\Amqp\Result;
|
||||||
|
use PhpAmqpLib\Exception\AMQPChannelClosedException;
|
||||||
|
use PhpAmqpLib\Message\AMQPMessage;
|
||||||
|
use App\Log\Log;
|
||||||
|
use Exception;
|
||||||
|
use PhpAmqpLib\Exception\AMQPConnectionClosedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午7:18
|
||||||
|
* Description: 消费者抽象基类.
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
abstract class BaseConsumer extends ConsumerMessage
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 消费者处理逻辑
|
||||||
|
* @param mixed $data
|
||||||
|
* @param AMQPMessage $message
|
||||||
|
* @return Result
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function consumeMessage($data, AMQPMessage $message): Result
|
||||||
|
{
|
||||||
|
$consumerClass = get_class($this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $this->handle($data);
|
||||||
|
} catch (AMQPChannelClosedException | AMQPConnectionClosedException $e) {
|
||||||
|
Log::get('queue', 'queue')->error("AMQP通道关闭异常 ($consumerClass): " . $e->getMessage(), [
|
||||||
|
'data' => $data,
|
||||||
|
'exception' => $e,
|
||||||
|
]);
|
||||||
|
// 可选:重连逻辑 or 丢弃
|
||||||
|
return Result::ACK; // 或 NACK
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::get('queue', 'queue')->error("AMQP消费者异常 ($consumerClass): " . $e->getMessage(), [
|
||||||
|
'data' => $data,
|
||||||
|
'exception' => $e,
|
||||||
|
]);
|
||||||
|
return Result::ACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子类实现的核心处理逻辑
|
||||||
|
* @param mixed $data
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
abstract protected function handle(mixed $data): Result;
|
||||||
|
}
|
18
app/Amqp/Producer/AliSlsProducer.php
Normal file
18
app/Amqp/Producer/AliSlsProducer.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Amqp\Producer;
|
||||||
|
|
||||||
|
use Hyperf\Amqp\Annotation\Producer;
|
||||||
|
use Hyperf\Amqp\Message\ProducerMessage;
|
||||||
|
|
||||||
|
#[Producer(exchange: 'wh_service_ali_sls', routingKey: 'wh_service_ali_sls_key')]
|
||||||
|
class AliSlsProducer extends ProducerMessage
|
||||||
|
{
|
||||||
|
public function __construct($data)
|
||||||
|
{
|
||||||
|
$this->payload = $data;
|
||||||
|
$this->properties['delivery_mode'] = 2; // 消息持久化
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
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;
|
||||||
|
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
36
app/JsonRpc/BaseService.php
Normal file
36
app/JsonRpc/BaseService.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/5
|
||||||
|
* * Time: 下午1:56
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\JsonRpc;
|
||||||
|
|
||||||
|
use App\Service\JsonRpcResponse;
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/5
|
||||||
|
* Time: 下午1:56
|
||||||
|
* Description: 服务基类.
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
abstract class BaseService
|
||||||
|
{
|
||||||
|
#[Inject]
|
||||||
|
protected JsonRpcResponse $response;
|
||||||
|
}
|
27
app/JsonRpc/InventoryService.php
Normal file
27
app/JsonRpc/InventoryService.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/5
|
||||||
|
* Time: 下午1:45
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\JsonRpc;
|
||||||
|
|
||||||
|
use Hyperf\RpcServer\Annotation\RpcService;
|
||||||
|
|
||||||
|
#[RpcService(name: 'InventoryService', server: 'jsonrpc-http', protocol: 'jsonrpc-http', publishTo: 'nacos')]
|
||||||
|
class InventoryService extends BaseService implements InventoryServiceInterface
|
||||||
|
{
|
||||||
|
public function addInventory(array $data): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
26
app/JsonRpc/InventoryServiceInterface.php
Normal file
26
app/JsonRpc/InventoryServiceInterface.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/5
|
||||||
|
* Time: 下午1:46
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\JsonRpc;
|
||||||
|
|
||||||
|
interface InventoryServiceInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 新增库存-手动单条.
|
||||||
|
* @param array $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addInventory(array $data): void;
|
||||||
|
}
|
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();
|
||||||
|
}
|
||||||
|
}
|
46
app/Log/AliSlsHandler.php
Normal file
46
app/Log/AliSlsHandler.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午6:33
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Log;
|
||||||
|
|
||||||
|
use App\Amqp\Producer\AliSlsProducer;
|
||||||
|
use Hyperf\Amqp\Producer;
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
use Monolog\Handler\AbstractProcessingHandler;
|
||||||
|
use Monolog\LogRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午7:19
|
||||||
|
* Description: 阿里云日志服务处理器.
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
class AliSlsHandler extends AbstractProcessingHandler
|
||||||
|
{
|
||||||
|
#[Inject]
|
||||||
|
protected Producer $producer;
|
||||||
|
|
||||||
|
protected function write(LogRecord $record): void
|
||||||
|
{
|
||||||
|
$logs = ['channel' => $record['channel'] ?? '', 'formatted' => $record['formatted'] ?? ''];
|
||||||
|
$message = new AliSlsProducer($logs);
|
||||||
|
$this->producer->produce($message, true);
|
||||||
|
}
|
||||||
|
}
|
44
app/Log/AppendRequestIdProcessor.php
Normal file
44
app/Log/AppendRequestIdProcessor.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午6:33
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Log;
|
||||||
|
|
||||||
|
use Hyperf\Context\Context;
|
||||||
|
use Hyperf\Coroutine\Coroutine;
|
||||||
|
use Monolog\LogRecord;
|
||||||
|
use Monolog\Processor\ProcessorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午7:19
|
||||||
|
* Description: 添加请求ID和协程ID
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
class AppendRequestIdProcessor implements ProcessorInterface
|
||||||
|
{
|
||||||
|
public const string REQUEST_ID = 'log.request.id';
|
||||||
|
|
||||||
|
public function __invoke(LogRecord $record): array|LogRecord
|
||||||
|
{
|
||||||
|
$record['extra']['request_id'] = Context::getOrSet(self::REQUEST_ID, uniqid('xw_cloud'));
|
||||||
|
$record['extra']['coroutine_id'] = Coroutine::id();
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
}
|
63
app/Log/Log.php
Normal file
63
app/Log/Log.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午6:42
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Log;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Hyperf\Context\ApplicationContext;
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
use Hyperf\Logger\LoggerFactory;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use function Hyperf\Support\make;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午7:19
|
||||||
|
* Description: 日志记录器类。
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
class Log
|
||||||
|
{
|
||||||
|
#[Inject]
|
||||||
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据提供的名称和分组获取日志记录器实例。
|
||||||
|
*
|
||||||
|
* @param string $name 日志记录器的名称,默认为'app'。
|
||||||
|
* @param string $group 日志记录器的分组,默认为'job'。
|
||||||
|
* @return LoggerInterface 返回一个日志记录器实例。
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function get(string $name = 'app', string $group = 'job'): LoggerInterface
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 尝试从应用上下文容器中获取LoggerFactory实例,并进一步获取指定名称和分组的日志记录器
|
||||||
|
return ApplicationContext::getContainer()->get(LoggerFactory::class)->get($name, $group);
|
||||||
|
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
|
||||||
|
// 如果在获取过程中发生异常,使用默认的日志记录器记录错误信息,并抛出ApiException
|
||||||
|
$logs = make(LoggerFactory::class)->get('default');
|
||||||
|
$logs->error($e->getMessage());
|
||||||
|
throw new Exception($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
app/Log/StdoutLoggerFactory.php
Normal file
48
app/Log/StdoutLoggerFactory.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午6:03
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Log;
|
||||||
|
|
||||||
|
use Hyperf\Context\ApplicationContext;
|
||||||
|
use Hyperf\Logger\LoggerFactory;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午6:04
|
||||||
|
* Description: 创建一个日志记录器
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
class StdoutLoggerFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @return LoggerInterface
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
*/
|
||||||
|
public function __invoke(ContainerInterface $container): LoggerInterface
|
||||||
|
{
|
||||||
|
return ApplicationContext::getContainer()->get(LoggerFactory::class)->get();
|
||||||
|
}
|
||||||
|
}
|
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
|
||||||
|
{
|
||||||
|
}
|
158
app/Service/AliLogsSignService.php
Normal file
158
app/Service/AliLogsSignService.php
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午7:13
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use Hyperf\Config\Annotation\Value;
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
use Hyperf\Guzzle\ClientFactory;
|
||||||
|
use function Hyperf\Coroutine\co;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/3
|
||||||
|
* Time: 下午7:18
|
||||||
|
* Description: 阿里云日志服务.
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
class AliLogsSignService
|
||||||
|
{
|
||||||
|
protected const string LOG_SIGNATURE_METHOD = 'hmac-sha1';
|
||||||
|
|
||||||
|
protected const string LOG_API_VERSION = '0.6.0';
|
||||||
|
|
||||||
|
#[Value('alibaba.accessKeyId')]
|
||||||
|
protected string $accessKeyId;
|
||||||
|
|
||||||
|
#[Value('alibaba.accessKeySecret')]
|
||||||
|
protected string $accessKeySecret;
|
||||||
|
|
||||||
|
#[Value('alibaba.logs')]
|
||||||
|
protected array $logs;
|
||||||
|
|
||||||
|
#[Inject]
|
||||||
|
protected ClientFactory $clientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名及请求头拼接.
|
||||||
|
* @param mixed $method
|
||||||
|
* @param mixed $uri
|
||||||
|
* @param mixed $params
|
||||||
|
* @param mixed $body
|
||||||
|
* @param mixed $logProject
|
||||||
|
* @param mixed $logEndpoint
|
||||||
|
*/
|
||||||
|
public function buildHeaders(
|
||||||
|
string $method,
|
||||||
|
string $uri,
|
||||||
|
array $params,
|
||||||
|
string $body,
|
||||||
|
string|object $logProject,
|
||||||
|
string $logEndpoint
|
||||||
|
): array
|
||||||
|
{
|
||||||
|
$headers = [
|
||||||
|
'x-log-signaturemethod' => self::LOG_SIGNATURE_METHOD,
|
||||||
|
'x-log-apiversion' => self::LOG_API_VERSION,
|
||||||
|
'Host' => sprintf('%s.%s', $logProject, $logEndpoint),
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
];
|
||||||
|
$contentLength = 0;
|
||||||
|
$contentMd5 = '';
|
||||||
|
if (!empty($body) && strlen($body) > 0) {
|
||||||
|
$contentLength = strlen($body);
|
||||||
|
$contentMd5 = strtoupper(md5($body));
|
||||||
|
$headers['Content-MD5'] = $contentMd5;
|
||||||
|
}
|
||||||
|
// date
|
||||||
|
setlocale(LC_TIME, 'en_US');
|
||||||
|
$date = gmdate('D, d M Y H:i:s \G\M\T', time());
|
||||||
|
$headers['Date'] = $date;
|
||||||
|
$headers['Content-Length'] = (string)$contentLength;
|
||||||
|
$contentType = $headers['Content-Type'];
|
||||||
|
$message = $method . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n";
|
||||||
|
// header
|
||||||
|
$filterHeaders = [];
|
||||||
|
foreach ($headers as $key => $val) {
|
||||||
|
if (str_starts_with($key, 'x-log-') || str_starts_with($key, 'x-acs-')) {
|
||||||
|
$filterHeaders[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksort($filterHeaders);
|
||||||
|
foreach ($filterHeaders as $key => $val) {
|
||||||
|
$message .= $key . ':' . $val . "\n";
|
||||||
|
}
|
||||||
|
// uri and params
|
||||||
|
$message .= $uri;
|
||||||
|
if (sizeof($params) > 0) {
|
||||||
|
$message .= '?';
|
||||||
|
}
|
||||||
|
ksort($params);
|
||||||
|
$sep = '';
|
||||||
|
foreach ($params as $key => $val) {
|
||||||
|
$message .= $sep . $key . '=' . $val;
|
||||||
|
$sep = '&';
|
||||||
|
}
|
||||||
|
// signature & authorization
|
||||||
|
$signature = $this->generateSignature($message);
|
||||||
|
$auth = 'LOG ' . $this->accessKeyId . ':' . $signature;
|
||||||
|
$headers['Authorization'] = $auth;
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现调用PutWebTracking接口将多条日志合并进行采集.
|
||||||
|
*/
|
||||||
|
public function putWebTracking(array $record): void
|
||||||
|
{
|
||||||
|
$logEndpoint = $this->logs['log_endpoint'];
|
||||||
|
$logProject = $this->logs['log_project'];
|
||||||
|
$logStores = $this->logs['log_store'];
|
||||||
|
$params = [];
|
||||||
|
$body = [
|
||||||
|
'__topic__' => 'mes api logs',
|
||||||
|
'__source__' => 'admin api',
|
||||||
|
'__logs__' => [
|
||||||
|
['Logs' => $record['formatted']],
|
||||||
|
],
|
||||||
|
'__tags__' => [
|
||||||
|
'mes' => $record['channel'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$body = json_encode($body);
|
||||||
|
|
||||||
|
$sign_url = sprintf('/logstores/%s/track', $logStores);
|
||||||
|
$headers = $this->buildHeaders('POST', $sign_url, $params, $body, $logProject, $logEndpoint);
|
||||||
|
$options = [
|
||||||
|
'headers' => $headers,
|
||||||
|
'body' => $body,
|
||||||
|
'query' => $params,
|
||||||
|
];
|
||||||
|
$url = sprintf('https://%s.%s/logstores/%s/track', $logProject, $logEndpoint, $logStores);
|
||||||
|
$client = $this->clientFactory->create();
|
||||||
|
co(function () use ($client, $url, $options) {
|
||||||
|
$client->request('POST', $url, $options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateSignature(string $message): string
|
||||||
|
{
|
||||||
|
return base64_encode(hash_hmac('sha1', $message, $this->accessKeySecret, true));
|
||||||
|
}
|
||||||
|
}
|
97
app/Service/JsonRpcResponse.php
Normal file
97
app/Service/JsonRpcResponse.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2024/6/3
|
||||||
|
* Time: 22:45
|
||||||
|
* Description:
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use Hyperf\Logger\LoggerFactory;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/1/3
|
||||||
|
* Time: 下午8:33
|
||||||
|
* Description: JsonRpc响应类.
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
class JsonRpcResponse
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* JsonRpc版本。
|
||||||
|
*/
|
||||||
|
private string $jsonRpcVersion = '2.0';
|
||||||
|
|
||||||
|
private mixed $id;
|
||||||
|
|
||||||
|
private mixed $result;
|
||||||
|
|
||||||
|
private mixed $error;
|
||||||
|
|
||||||
|
protected LoggerInterface $logger;
|
||||||
|
|
||||||
|
public function __construct(LoggerFactory $loggerFactory, $id = null, $result = null, $error = null)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->result = $result;
|
||||||
|
$this->error = $error;
|
||||||
|
|
||||||
|
$this->logger = $loggerFactory->get('jsonrpc');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withId($id): static
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withResult($result): static
|
||||||
|
{
|
||||||
|
$this->result = $result;
|
||||||
|
|
||||||
|
$this->logger->info(json_encode(['RPC result' => $result], JSON_UNESCAPED_UNICODE));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withError($code, $message, $data = null): static
|
||||||
|
{
|
||||||
|
$this->error = [
|
||||||
|
'code' => $code,
|
||||||
|
'message' => $message,
|
||||||
|
'data' => $data,
|
||||||
|
];
|
||||||
|
$this->logger->error(json_encode(['RPC error' => $message], JSON_UNESCAPED_UNICODE));
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
$response = [
|
||||||
|
'jsonrpc' => $this->jsonRpcVersion,
|
||||||
|
'id' => $this->id,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->error) {
|
||||||
|
$response['error'] = $this->error;
|
||||||
|
} else {
|
||||||
|
$response['result'] = $this->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toJson(): bool|string
|
||||||
|
{
|
||||||
|
return json_encode($this->toArray());
|
||||||
|
}
|
||||||
|
}
|
44
app/Service/MigrateService.php
Normal file
44
app/Service/MigrateService.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/6/4
|
||||||
|
* Time: 下午1:57
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use Hyperf\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
class MigrateService
|
||||||
|
{
|
||||||
|
/***
|
||||||
|
* 在创建的时候自动添加如下字段
|
||||||
|
* @param Blueprint $blueprint
|
||||||
|
* @return Blueprint
|
||||||
|
*/
|
||||||
|
public static function migrateCreateInfo(Blueprint $blueprint): Blueprint
|
||||||
|
{
|
||||||
|
$blueprint->integer('creator_id')->default(0)->unsigned()->comment('创建人ID');
|
||||||
|
$blueprint->string('creator_name', 45)->default('')->comment('创建人姓名');
|
||||||
|
$blueprint->integer('created_at')->default(0)->unsigned()->comment('创建时间');
|
||||||
|
$blueprint->integer('updated_at')->default(0)->unsigned()->comment('更新时间');
|
||||||
|
$blueprint->integer('deleted_at')->nullable()->unsigned()->comment('软删除时间');
|
||||||
|
return $blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function migrateTime(Blueprint $blueprint): Blueprint
|
||||||
|
{
|
||||||
|
$blueprint->integer('created_at')->default(0)->unsigned()->comment('创建时间');
|
||||||
|
$blueprint->integer('updated_at')->default(0)->unsigned()->comment('更新时间');
|
||||||
|
$blueprint->integer('deleted_at')->nullable()->unsigned()->comment('软删除时间');
|
||||||
|
return $blueprint;
|
||||||
|
}
|
||||||
|
}
|
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();
|
||||||
|
})();
|
88
composer.json
Normal file
88
composer.json
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"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.3",
|
||||||
|
"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/json-rpc": "~3.1.0",
|
||||||
|
"hyperf/logger": "~3.1.0",
|
||||||
|
"hyperf/memory": "~3.1.0",
|
||||||
|
"hyperf/model-cache": "~3.1.0",
|
||||||
|
"hyperf/process": "~3.1.0",
|
||||||
|
"hyperf/redis": "~3.1.0",
|
||||||
|
"hyperf/rpc": "~3.1.0",
|
||||||
|
"hyperf/rpc-client": "~3.1.0",
|
||||||
|
"hyperf/rpc-server": "~3.1.0",
|
||||||
|
"hyperf/service-governance": "~3.1.0",
|
||||||
|
"hyperf/service-governance-nacos": "^3.1"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
10360
composer.lock
generated
Normal file
10360
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' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
18
config/autoload/dependencies.php
Normal file
18
config/autoload/dependencies.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 App\Log\StdoutLoggerFactory;
|
||||||
|
use Hyperf\Contract\StdoutLoggerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
StdoutLoggerInterface::class => StdoutLoggerFactory::class,
|
||||||
|
];
|
44
config/autoload/devtool.php
Normal file
44
config/autoload/devtool.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?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 [
|
||||||
|
'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',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
19
config/autoload/exceptions.php
Normal file
19
config/autoload/exceptions.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 [
|
||||||
|
'handler' => [
|
||||||
|
'http' => [
|
||||||
|
Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
|
||||||
|
App\Exception\Handler\AppExceptionHandler::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
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,
|
||||||
|
];
|
81
config/autoload/logger.php
Normal file
81
config/autoload/logger.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?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 App\Log\AliSlsHandler;
|
||||||
|
use App\Log\AppendRequestIdProcessor;
|
||||||
|
use Monolog\Formatter\JsonFormatter;
|
||||||
|
use Monolog\Formatter\LineFormatter;
|
||||||
|
use Monolog\Handler\RotatingFileHandler;
|
||||||
|
use Psr\Log\LogLevel;
|
||||||
|
use function Hyperf\Support\env;
|
||||||
|
|
||||||
|
$env = env('APP_ENV', 'production');
|
||||||
|
$isProduction = $env === 'production';
|
||||||
|
$productionLogLevel = env('LOG_LEVEL', 'error');
|
||||||
|
|
||||||
|
$formatter = $isProduction
|
||||||
|
? [
|
||||||
|
'class' => JsonFormatter::class,
|
||||||
|
'constructor' => [
|
||||||
|
'format' => null,
|
||||||
|
'dateFormat' => 'Y-m-d H:i:s',
|
||||||
|
'allowInlineLineBreaks' => true,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
'class' => LineFormatter::class,
|
||||||
|
'constructor' => [
|
||||||
|
'format' => "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n",
|
||||||
|
'dateFormat' => 'Y-m-d H:i:s',
|
||||||
|
'allowInlineLineBreaks' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$loggers = [
|
||||||
|
'default', 'sql', 'request', 'crontab', 'queue', 'timer', 'job', 'some', 'amqp'
|
||||||
|
];
|
||||||
|
|
||||||
|
$logLevel = $isProduction ? $productionLogLevel : LogLevel::INFO;
|
||||||
|
|
||||||
|
$config = [];
|
||||||
|
|
||||||
|
foreach ($loggers as $loggerName) {
|
||||||
|
$handler = $isProduction
|
||||||
|
? [
|
||||||
|
'class' => AliSlsHandler::class,
|
||||||
|
'constructor' => [
|
||||||
|
'filename' => BASE_PATH . sprintf('/runtime/logs/%s/%s.log', $loggerName, $loggerName),
|
||||||
|
'level' => $logLevel,
|
||||||
|
'maxFiles' => 30,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
'class' => RotatingFileHandler::class,
|
||||||
|
'constructor' => [
|
||||||
|
'filename' => BASE_PATH . sprintf('/runtime/logs/%s/%s.log', $loggerName, $loggerName),
|
||||||
|
'level' => $logLevel,
|
||||||
|
'maxFiles' => 30,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$config[$loggerName] = [
|
||||||
|
'handler' => $handler,
|
||||||
|
'formatter' => $formatter,
|
||||||
|
'processors' => [
|
||||||
|
[
|
||||||
|
'class' => AppendRequestIdProcessor::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
15
config/autoload/middlewares.php
Normal file
15
config/autoload/middlewares.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 [
|
||||||
|
'http' => [
|
||||||
|
],
|
||||||
|
];
|
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),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
52
config/autoload/server.php
Normal file
52
config/autoload/server.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?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\JsonRpc\HttpServer;
|
||||||
|
use Hyperf\Server\Event;
|
||||||
|
use Hyperf\Server\ServerInterface;
|
||||||
|
use Swoole\Constant;
|
||||||
|
use function Hyperf\Support\env;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'mode' => SWOOLE_PROCESS,
|
||||||
|
'servers' => [
|
||||||
|
[
|
||||||
|
'name' => 'jsonrpc-http',
|
||||||
|
'type' => ServerInterface::SERVER_HTTP,
|
||||||
|
'host' => '0.0.0.0',
|
||||||
|
'port' => intval(env('HTTP_PORT', 9507)),
|
||||||
|
'sock_type' => SWOOLE_SOCK_TCP,
|
||||||
|
'callbacks' => [
|
||||||
|
Event::ON_REQUEST => [HttpServer::class, 'onRequest'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'settings' => [
|
||||||
|
Constant::OPTION_ENABLE_COROUTINE => true,
|
||||||
|
Constant::OPTION_WORKER_NUM => min(swoole_cpu_num() * 4, 32),
|
||||||
|
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,
|
||||||
|
|
||||||
|
// 连接池配置
|
||||||
|
Constant::OPTION_REACTOR_NUM => swoole_cpu_num(),
|
||||||
|
],
|
||||||
|
'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'],
|
||||||
|
],
|
||||||
|
];
|
32
config/autoload/services.php
Normal file
32
config/autoload/services.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2024/5/31
|
||||||
|
* Time: 08:40
|
||||||
|
* Description:
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'enable' => [
|
||||||
|
'discovery' => true,
|
||||||
|
'register' => true,
|
||||||
|
],
|
||||||
|
'consumers' => [],
|
||||||
|
|
||||||
|
'providers' => [],
|
||||||
|
'drivers' => [
|
||||||
|
'nacos' => [
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'port' => 8848,
|
||||||
|
// The nacos account info
|
||||||
|
'username' => null,
|
||||||
|
'password' => null,
|
||||||
|
'guzzle' => [
|
||||||
|
'config' => null,
|
||||||
|
],
|
||||||
|
'heartbeat' => 5,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
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
|
116
migrations/2025_06_05_150817_create_inventory_table.php
Normal file
116
migrations/2025_06_05_150817_create_inventory_table.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/3/7
|
||||||
|
* Time: 下午3:27
|
||||||
|
* Description: 创建库存表 inventory 的迁移文件
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Service\MigrateService;
|
||||||
|
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('inventory', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
|
||||||
|
$table->string('company_code', 50)->default('')->comment('公司代码');
|
||||||
|
$table->tinyInteger('status')->default(1)->comment('资源状态(-1:删除,0:禁用,1:在途,2:已入库,3:部分出库,4:全部出库,5:待分配库位)');
|
||||||
|
$table->integer('bale_id')->default(0)->comment('采购捆包id');
|
||||||
|
$table->tinyInteger('source')->default(1)->comment('数据来源(1:自建,2:仓储)');
|
||||||
|
$table->integer('stock_id')->default(0)->comment('库存汇总ID');
|
||||||
|
$table->string('bale_no', 45)->default('')->comment('捆包号');
|
||||||
|
$table->string('supplier', 50)->default('')->comment('供应商');
|
||||||
|
$table->integer('supplier_id')->default(0)->comment('供应商id');
|
||||||
|
$table->string('issuing', 50)->default('')->comment('开证公司');
|
||||||
|
$table->integer('issuing_id')->default(0)->comment('开证公司id');
|
||||||
|
$table->string('breed', 50)->default('')->comment('商品名称');
|
||||||
|
$table->integer('breed_id')->default(0)->comment('品名id');
|
||||||
|
$table->string('brand', 50)->default('')->comment('品牌');
|
||||||
|
$table->integer('brand_id')->default(0)->comment('品牌id');
|
||||||
|
$table->string('grade', 50)->default('')->comment('等级');
|
||||||
|
$table->integer('grade_id')->default(0)->comment('等级id');
|
||||||
|
$table->string('thickness', 50)->default('')->comment('厚度');
|
||||||
|
$table->string('width', 50)->default('')->comment('宽度');
|
||||||
|
$table->string('length', 50)->default('')->comment('长度');
|
||||||
|
$table->string('pieces', 50)->default('')->comment('片数');
|
||||||
|
$table->decimal('per_cube', 18, 9)->comment('单包立方');
|
||||||
|
$table->unsignedInteger('init_number')->default(0)->comment('入库初始件数');
|
||||||
|
$table->unsignedInteger('number')->default(0)->comment('库存件数');
|
||||||
|
$table->unsignedInteger('lock_number')->default(0)->comment('锁定件数');
|
||||||
|
$table->unsignedInteger('enable_number')->default(0)->comment('可用件数');
|
||||||
|
$table->unsignedInteger('out_number')->default(0)->comment('已出件数');
|
||||||
|
$table->decimal('foreign_currency_price', 18, 2)->default(0.00)->comment('外币单价');
|
||||||
|
$table->integer('foreign_currency_id')->default(0)->comment('外币名称id');
|
||||||
|
$table->string('foreign_currency_unit', 3)->comment('外币符号');
|
||||||
|
$table->decimal('purchase_amount', 18, 2)->default(0.00)->comment('采购金额');
|
||||||
|
$table->decimal('sales_price', 18, 2)->default(0.00)->comment('销售单价/挂牌价');
|
||||||
|
$table->decimal('pay_rate', 18, 4)->default(0.0000)->comment('实付汇率');
|
||||||
|
$table->decimal('purchase_price', 18, 2)->default(0.00)->comment('采购单价');
|
||||||
|
$table->decimal('cost_price', 18, 2)->comment('费用平摊单价');
|
||||||
|
$table->integer('storage_location_id')->default(0)->comment('库位ID');
|
||||||
|
$table->string('storage_location', 100)->default('')->comment('库位');
|
||||||
|
$table->string('contract_no', 45)->default('')->comment('合同号');
|
||||||
|
$table->string('bill_lading_no', 45)->default('')->comment('提单号');
|
||||||
|
$table->integer('port_id')->default(0)->comment('港口id');
|
||||||
|
$table->string('purpose_port', 255)->default('')->comment('目的港');
|
||||||
|
$table->integer('expected_storage_time')->default(0)->comment('预计入库时间');
|
||||||
|
$table->integer('expected_arrival_time')->default(0)->comment('预计到港时间');
|
||||||
|
$table->string('place_origin', 255)->default('')->comment('产地');
|
||||||
|
$table->tinyInteger('is_changed')->default(0)->comment('是否改动(0:未改动,1:已改动)');
|
||||||
|
$table->tinyInteger('qr_code_status')->default(0)->comment('扫码状态(1-未扫码,2-已扫码)');
|
||||||
|
$table->tinyInteger('purchase_type')->default(0)->comment('采购类型 (1 合同采购, 2外调采购)');
|
||||||
|
$table->json('images')->comment('产品图片');
|
||||||
|
$table->string('thumbnail', 255)->default('')->comment('封面缩略图');
|
||||||
|
$table->integer('inbound_time')->default(0)->comment('入库时间');
|
||||||
|
$table->integer('stocker_id')->default(0)->comment('入库人id');
|
||||||
|
$table->string('stocker_name', 30)->default('')->comment('入库人名字');
|
||||||
|
$table->string('cargo_remark', 200)->default('')->comment('货物状态');
|
||||||
|
|
||||||
|
// 仓库字段
|
||||||
|
$table->string('purchase_sn', 45)->default('')->comment('采购单号');
|
||||||
|
$table->string('contract_sn', 45)->default('')->comment('采购合同号');
|
||||||
|
$table->string('container_no', 45)->default('')->comment('柜号/车牌号');
|
||||||
|
$table->tinyInteger('customer_type')->default(1)->comment('代理类型:1-代理 2-非代理');
|
||||||
|
$table->tinyInteger('category')->default(1)->comment('货物类型:1-原木 2-板材');
|
||||||
|
$table->tinyInteger('packaging_type')->default(1)->comment('包装类型(1-整包、2-混包)');
|
||||||
|
$table->string('spec', 100)->default('')->comment('仓库规格');
|
||||||
|
$table->integer('sh_company_id')->default(0)->unsigned()->comment('收货公司id');
|
||||||
|
$table->string('sh_company_name', 45)->default('')->comment('收货公司');
|
||||||
|
$table->integer('aisle_id')->default(0)->unsigned()->comment('通道ID');
|
||||||
|
$table->string('aisle_name', 45)->default('')->comment('通道名');
|
||||||
|
$table->integer('warehouse_id')->default(0)->comment('仓库id');
|
||||||
|
$table->string('warehouse', 255)->default('')->comment('仓库');
|
||||||
|
$table->integer('location_id')->default(0)->unsigned()->comment('仓库库位ID');
|
||||||
|
$table->integer('attribute_id')->default(0)->unsigned()->comment('库位属性ID');
|
||||||
|
$table->string('custom', 45)->default('')->comment('自定义属性');
|
||||||
|
$table->string('location_no', 100)->default('')->comment('仓库库位拼接信息');
|
||||||
|
$table->string('remark', 300)->default('')->comment('备注');
|
||||||
|
|
||||||
|
MigrateService::migrateCreateInfo($table);
|
||||||
|
|
||||||
|
$table->comment('库存表');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('inventory');
|
||||||
|
}
|
||||||
|
};
|
76
migrations/2025_06_05_154322_create_stock_table.php
Normal file
76
migrations/2025_06_05_154322_create_stock_table.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Author: ykxiao
|
||||||
|
* Date: 2025/3/7
|
||||||
|
* Time: 下午3:27
|
||||||
|
* Description: 创建库存汇总表 stock 的迁移文件
|
||||||
|
*
|
||||||
|
* (c) ykxiao <yk_9001@hotmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Service\MigrateService;
|
||||||
|
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('stock', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
|
||||||
|
$table->string('company_code', 50)->default('')->comment('公司代码');
|
||||||
|
|
||||||
|
$table->integer('issuing_id')->default(0)->comment('开证公司id');
|
||||||
|
$table->integer('sh_company_id')->default(0)->unsigned()->comment('收货公司id');
|
||||||
|
$table->tinyInteger('source')->default(0)->comment('数据来源(1:自建,2:仓储)');
|
||||||
|
$table->tinyInteger('type')->default(1)->comment('汇总类型(1:在途,2现货)');
|
||||||
|
$table->integer('warehouse_id')->default(0)->comment('仓库ID');
|
||||||
|
$table->string('warehouse', 255)->default('')->comment('仓库');
|
||||||
|
$table->string('aisle_id', 50)->default('')->comment('通道号');
|
||||||
|
$table->string('aisle_name', 45)->default('')->comment('通道名');
|
||||||
|
$table->integer('breed_id')->default(0)->comment('品名ID');
|
||||||
|
$table->string('breed', 50)->default('')->comment('品名');
|
||||||
|
$table->integer('brand_id')->default(0)->comment('品牌ID');
|
||||||
|
$table->string('brand', 50)->default('')->comment('品牌');
|
||||||
|
$table->integer('grade_id')->default(0)->comment('等级ID');
|
||||||
|
$table->string('grade', 50)->default('')->comment('等级');
|
||||||
|
$table->string('thickness', 50)->default('')->comment('厚度');
|
||||||
|
$table->string('width', 50)->default('')->comment('宽度');
|
||||||
|
$table->string('length', 50)->default('')->comment('长度');
|
||||||
|
$table->decimal('min_price', 18)->default(0.00)->comment('最低价');
|
||||||
|
$table->decimal('sales_price', 18)->default(0.00)->comment('挂牌价');
|
||||||
|
$table->integer('number')->default(0)->comment('库存件数');
|
||||||
|
$table->integer('enable_number')->default(0)->comment('可用件数');
|
||||||
|
$table->integer('lock_number')->default(0)->comment('锁定件数');
|
||||||
|
$table->unsignedDecimal('cube', 18, 9)->default(0.000000000)->comment('库存方数');
|
||||||
|
$table->unsignedTinyInteger('sales_status')->default(1)->comment('销售状态(1:销售中,2:封盘中,3:隐藏中)');
|
||||||
|
$table->tinyInteger('status')->default(1)->comment('库存状态(0:禁用,1:正常)');
|
||||||
|
$table->integer('create_time')->default(0)->comment('创建时间');
|
||||||
|
$table->integer('update_time')->default(0)->comment('更新时间');
|
||||||
|
$table->integer('delete_time')->default(0)->comment('删除时间');
|
||||||
|
|
||||||
|
$table->unique(['company_code', 'source', 'type', 'warehouse_id', 'breed_id', 'brand_id', 'grade_id', 'thickness', 'width', 'length', 'aisle_id'], 'idx_unique');
|
||||||
|
$table->index(['company_code', 'breed_id', 'brand_id', 'grade_id', 'thickness', 'width', 'length'], 'idx_spec');
|
||||||
|
|
||||||
|
MigrateService::migrateTime($table);
|
||||||
|
|
||||||
|
$table->comment('库存汇总表');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('stock');
|
||||||
|
}
|
||||||
|
};
|
14
phpstan.neon.dist
Normal file
14
phpstan.neon.dist
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Magic behaviour with __get, __set, __call and __callStatic is not exactly static analyser-friendly :)
|
||||||
|
# Fortunately, You can ignore it by the following config.
|
||||||
|
#
|
||||||
|
# vendor/bin/phpstan analyse app --memory-limit 200M -l 0
|
||||||
|
#
|
||||||
|
parameters:
|
||||||
|
level: 0
|
||||||
|
paths:
|
||||||
|
- ./app
|
||||||
|
- ./config
|
||||||
|
reportUnmatchedIgnoredErrors: false
|
||||||
|
ignoreErrors:
|
||||||
|
- '#Static call to instance method Hyperf\\HttpServer\\Router\\Router::[a-zA-Z0-9\\_]+\(\)#'
|
||||||
|
- '#Static call to instance method Hyperf\\DbConnection\\Db::[a-zA-Z0-9\\_]+\(\)#'
|
16
phpunit.xml.dist
Normal file
16
phpunit.xml.dist
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="./test/bootstrap.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Tests">
|
||||||
|
<directory suffix="Test.php">./test</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<php>
|
||||||
|
<env name="APP_ENV" value="testing" force="true"/>
|
||||||
|
</php>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">./app</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
27
test/Cases/ExampleTest.php
Normal file
27
test/Cases/ExampleTest.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 HyperfTest\Cases;
|
||||||
|
|
||||||
|
use Hyperf\Testing\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ExampleTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testExample()
|
||||||
|
{
|
||||||
|
$this->get('/')->assertOk()->assertSee('Hyperf');
|
||||||
|
}
|
||||||
|
}
|
45
test/HttpTestCase.php
Normal file
45
test/HttpTestCase.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?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 HyperfTest;
|
||||||
|
|
||||||
|
use Hyperf\Testing\Client;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
use function Hyperf\Support\make;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HttpTestCase.
|
||||||
|
* @method get($uri, $data = [], $headers = [])
|
||||||
|
* @method post($uri, $data = [], $headers = [])
|
||||||
|
* @method json($uri, $data = [], $headers = [])
|
||||||
|
* @method file($uri, $data = [], $headers = [])
|
||||||
|
* @method request($method, $path, $options = [])
|
||||||
|
*/
|
||||||
|
abstract class HttpTestCase extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
public function __construct($name = null, array $data = [], $dataName = '')
|
||||||
|
{
|
||||||
|
parent::__construct($name, $data, $dataName);
|
||||||
|
$this->client = make(Client::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($name, $arguments)
|
||||||
|
{
|
||||||
|
return $this->client->{$name}(...$arguments);
|
||||||
|
}
|
||||||
|
}
|
30
test/bootstrap.php
Normal file
30
test/bootstrap.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
|
||||||
|
*/
|
||||||
|
ini_set('display_errors', 'on');
|
||||||
|
ini_set('display_startup_errors', 'on');
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
date_default_timezone_set('Asia/Shanghai');
|
||||||
|
|
||||||
|
Swoole\Runtime::enableCoroutine(true);
|
||||||
|
|
||||||
|
! 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());
|
||||||
|
|
||||||
|
Hyperf\Di\ClassLoader::init();
|
||||||
|
|
||||||
|
$container = require BASE_PATH . '/config/container.php';
|
||||||
|
|
||||||
|
$container->get(Hyperf\Contract\ApplicationInterface::class);
|
Reference in New Issue
Block a user