基于Hyperf实现简易消息推送系统代码上线
This commit is contained in:
commit
ef4a9d4688
17
hyperf-skeleton/.env.example
Normal file
17
hyperf-skeleton/.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
|
13
hyperf-skeleton/.gitignore
vendored
Normal file
13
hyperf-skeleton/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.buildpath
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
*.patch
|
||||||
|
.idea/
|
||||||
|
.git/
|
||||||
|
runtime/
|
||||||
|
vendor/
|
||||||
|
.phpintel/
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
|
*.lock
|
||||||
|
.phpunit*
|
60
hyperf-skeleton/Dockerfile
Normal file
60
hyperf-skeleton/Dockerfile
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Default Dockerfile
|
||||||
|
#
|
||||||
|
# @link https://www.hyperf.io
|
||||||
|
# @document https://hyperf.wiki
|
||||||
|
# @contact group@hyperf.io
|
||||||
|
# @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||||
|
|
||||||
|
FROM hyperf/hyperf:7.4-alpine-v3.11-cli
|
||||||
|
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"} \
|
||||||
|
COMPOSER_VERSION=1.10.10 \
|
||||||
|
APP_ENV=prod \
|
||||||
|
SCAN_CACHEABLE=(true)
|
||||||
|
|
||||||
|
# update
|
||||||
|
RUN set -ex \
|
||||||
|
# install composer
|
||||||
|
&& cd /tmp \
|
||||||
|
&& wget https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar \
|
||||||
|
&& chmod u+x composer.phar \
|
||||||
|
&& mv composer.phar /usr/local/bin/composer \
|
||||||
|
# show php version and extensions
|
||||||
|
&& php -v \
|
||||||
|
&& php -m \
|
||||||
|
&& php --ri swoole \
|
||||||
|
# ---------- some config ----------
|
||||||
|
&& cd /etc/php7 \
|
||||||
|
# - 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"]
|
36
hyperf-skeleton/README.md
Normal file
36
hyperf-skeleton/README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# 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 >= 7.2
|
||||||
|
- Swoole PHP extension >= 4.4,and Disabled `Short Name`
|
||||||
|
- OpenSSL PHP extension
|
||||||
|
- JSON PHP extension
|
||||||
|
- PDO PHP extension (If you need to use MySQL Client)
|
||||||
|
- Redis PHP extension (If you need to use Redis Client)
|
||||||
|
- Protobuf PHP extension (If you need to use gRPC Server of Client)
|
||||||
|
|
||||||
|
# Installation using Composer
|
||||||
|
|
||||||
|
The easiest way to create a new Hyperf project is to use Composer. If you don't have it already installed, then please install as per the documentation.
|
||||||
|
|
||||||
|
To create your new Hyperf project:
|
||||||
|
|
||||||
|
$ composer create-project hyperf/hyperf-skeleton path/to/install
|
||||||
|
|
||||||
|
Once installed, you can run the server immediately using the command below.
|
||||||
|
|
||||||
|
$ cd path/to/install
|
||||||
|
$ php bin/hyperf.php start
|
||||||
|
|
||||||
|
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.
|
36
hyperf-skeleton/app/Amqp/Consumer/DemoConsumer.php
Normal file
36
hyperf-skeleton/app/Amqp/Consumer/DemoConsumer.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Amqp\Consumer;
|
||||||
|
|
||||||
|
use Hyperf\Amqp\Annotation\Consumer;
|
||||||
|
use Hyperf\Amqp\Message\ConsumerMessage;
|
||||||
|
use Hyperf\Amqp\Result;
|
||||||
|
use Hyperf\Server\ServerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Consumer(exchange="hyperf", routingKey="hyperf", queue="hyperf", nums=1)
|
||||||
|
*/
|
||||||
|
class DemoConsumer extends ConsumerMessage
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* RabbitMQ消费端代码
|
||||||
|
* @param array $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function consume($data): string
|
||||||
|
{
|
||||||
|
// 获取集合中所有的value
|
||||||
|
$redis = $this->container->get(\Redis::class);
|
||||||
|
$fdList = $redis->sMembers('websocket_sjd_1');
|
||||||
|
|
||||||
|
$server = $this->container->get(ServerFactory::class)->getServer()->getServer();
|
||||||
|
foreach ($fdList as $fd) {
|
||||||
|
if (!empty($fd)) {
|
||||||
|
$server->push((int)$fd, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result::ACK;
|
||||||
|
}
|
||||||
|
}
|
22
hyperf-skeleton/app/Amqp/Producer/DemoProducer.php
Normal file
22
hyperf-skeleton/app/Amqp/Producer/DemoProducer.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Amqp\Producer;
|
||||||
|
|
||||||
|
use Hyperf\Amqp\Annotation\Producer;
|
||||||
|
use Hyperf\Amqp\Message\ProducerMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Producer(exchange="hyperf", routingKey="hyperf")
|
||||||
|
*/
|
||||||
|
class DemoProducer extends ProducerMessage
|
||||||
|
{
|
||||||
|
public function __construct($data)
|
||||||
|
{
|
||||||
|
// 设置不同 pool
|
||||||
|
$this->poolName = 'default';
|
||||||
|
|
||||||
|
// 具体丢入队列中的信息
|
||||||
|
$this->payload = $data;
|
||||||
|
}
|
||||||
|
}
|
26
hyperf-skeleton/app/Constants/ErrorCode.php
Normal file
26
hyperf-skeleton/app/Constants/ErrorCode.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?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!")
|
||||||
|
*/
|
||||||
|
const SERVER_ERROR = 500;
|
||||||
|
}
|
38
hyperf-skeleton/app/Controller/AbstractController.php
Normal file
38
hyperf-skeleton/app/Controller/AbstractController.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\Controller;
|
||||||
|
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||||
|
use Hyperf\HttpServer\Contract\ResponseInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
abstract class AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Inject
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
protected $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Inject
|
||||||
|
* @var RequestInterface
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Inject
|
||||||
|
* @var ResponseInterface
|
||||||
|
*/
|
||||||
|
protected $response;
|
||||||
|
}
|
26
hyperf-skeleton/app/Controller/IndexController.php
Normal file
26
hyperf-skeleton/app/Controller/IndexController.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?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}.",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
45
hyperf-skeleton/app/Controller/MainController.php
Normal file
45
hyperf-skeleton/app/Controller/MainController.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Amqp\Producer\DemoProducer;
|
||||||
|
use Hyperf\Amqp\Producer;
|
||||||
|
use Hyperf\Utils\ApplicationContext;
|
||||||
|
|
||||||
|
class MainController extends AbstractController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* index
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function index() : array
|
||||||
|
{
|
||||||
|
$name = $this->request->input('name', 'Hyperf');
|
||||||
|
$num = $this->request->input('num', 'test12345');
|
||||||
|
$door = $this->request->input('door', '办公室');
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'code' => 200,
|
||||||
|
'data' => [
|
||||||
|
'userOutName' => $name,
|
||||||
|
'userOutNum' => $num,
|
||||||
|
'recordOutTime' => date('Y-m-d H:i:s'),
|
||||||
|
'doorOutName' => $door
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$data = json_encode($data);
|
||||||
|
// 组装队列消息
|
||||||
|
$message = new DemoProducer($data);
|
||||||
|
// 通过容器获取队列生产者实例
|
||||||
|
$producer = ApplicationContext::getContainer()->get(Producer::class);
|
||||||
|
// 生产消息
|
||||||
|
$result = $producer->produce($message);
|
||||||
|
var_dump($result);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'code' => 200,
|
||||||
|
'msg' => "Push success!",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
66
hyperf-skeleton/app/Controller/WebSocketController.php
Normal file
66
hyperf-skeleton/app/Controller/WebSocketController.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Hyperf\Contract\OnCloseInterface;
|
||||||
|
use Hyperf\Contract\OnMessageInterface;
|
||||||
|
use Hyperf\Contract\OnOpenInterface;
|
||||||
|
use Swoole\Http\Request;
|
||||||
|
use Swoole\Websocket\Frame;
|
||||||
|
|
||||||
|
class WebSocketController extends AbstractController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
* @param $server
|
||||||
|
* @param Frame $frame
|
||||||
|
*/
|
||||||
|
public function onMessage($server, Frame $frame): void
|
||||||
|
{
|
||||||
|
$redis = $this->container->get(\Redis::class);
|
||||||
|
// 获取所有的客户端id
|
||||||
|
$fdList = $redis->sMembers('websocket_sjd_1');
|
||||||
|
// 如果当前客户端在客户端集合中,就刷新
|
||||||
|
if (in_array($frame->fd, $fdList)) {
|
||||||
|
$redis->sAdd('websocket_sjd_1', $frame->fd);
|
||||||
|
$redis->expire('websocket_sjd_1', 7200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// push消息给客户端
|
||||||
|
$server->push($frame->fd, json_encode(['Recv' => $frame->data]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端失去连接
|
||||||
|
* @param $server
|
||||||
|
* @param int $fd
|
||||||
|
* @param int $reactorId
|
||||||
|
*/
|
||||||
|
public function onClose($server, int $fd, int $reactorId): void
|
||||||
|
{
|
||||||
|
$redis = $this->container->get(\Redis::class);
|
||||||
|
// 移除集合中指定的value
|
||||||
|
$redis->sRem('websocket_sjd_1', $fd);
|
||||||
|
echo 'closed';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端连接开启
|
||||||
|
* @param $server
|
||||||
|
* @param Request $request
|
||||||
|
*/
|
||||||
|
public function onOpen($server, Request $request): void
|
||||||
|
{
|
||||||
|
$redis = $this->container->get(\Redis::class);
|
||||||
|
// 保存客户端id
|
||||||
|
$resAdd = $redis->sAdd('websocket_sjd_1', $request->fd);
|
||||||
|
var_dump($resAdd);
|
||||||
|
|
||||||
|
$resExp = $redis->expire('websocket_sjd_1', 7200);
|
||||||
|
var_dump($resExp);
|
||||||
|
|
||||||
|
// push消息给客户端
|
||||||
|
$server->push($request->fd, json_encode(['Status' => 'Opened']));
|
||||||
|
}
|
||||||
|
}
|
28
hyperf-skeleton/app/Exception/BusinessException.php
Normal file
28
hyperf-skeleton/app/Exception/BusinessException.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
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<?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
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var StdoutLoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
public function __construct(StdoutLoggerInterface $logger)
|
||||||
|
{
|
||||||
|
$this->logger = $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;
|
||||||
|
}
|
||||||
|
}
|
61
hyperf-skeleton/app/Listener/DbQueryExecutedListener.php
Normal file
61
hyperf-skeleton/app/Listener/DbQueryExecutedListener.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?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\Database\Events\QueryExecuted;
|
||||||
|
use Hyperf\Event\Annotation\Listener;
|
||||||
|
use Hyperf\Event\Contract\ListenerInterface;
|
||||||
|
use Hyperf\Logger\LoggerFactory;
|
||||||
|
use Hyperf\Utils\Arr;
|
||||||
|
use Hyperf\Utils\Str;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if ($event instanceof QueryExecuted) {
|
||||||
|
$sql = $event->sql;
|
||||||
|
if (! Arr::isAssoc($event->bindings)) {
|
||||||
|
foreach ($event->bindings as $key => $value) {
|
||||||
|
$sql = Str::replaceFirst('?', "'{$value}'", $sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->info(sprintf('[%s] %s', $event->time, $sql));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
hyperf-skeleton/app/Listener/QueueHandleListener.php
Normal file
79
hyperf-skeleton/app/Listener/QueueHandleListener.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?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\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\ExceptionHandler\Formatter\FormatterInterface;
|
||||||
|
use Hyperf\Logger\LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Listener
|
||||||
|
*/
|
||||||
|
class QueueHandleListener implements ListenerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Psr\Log\LoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FormatterInterface
|
||||||
|
*/
|
||||||
|
protected $formatter;
|
||||||
|
|
||||||
|
public function __construct(LoggerFactory $loggerFactory, FormatterInterface $formatter)
|
||||||
|
{
|
||||||
|
$this->logger = $loggerFactory->get('queue');
|
||||||
|
$this->formatter = $formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listen(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
AfterHandle::class,
|
||||||
|
BeforeHandle::class,
|
||||||
|
FailedHandle::class,
|
||||||
|
RetryHandle::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(object $event)
|
||||||
|
{
|
||||||
|
if ($event instanceof Event && $event->message->job()) {
|
||||||
|
$job = $event->message->job();
|
||||||
|
$jobClass = get_class($job);
|
||||||
|
$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($this->formatter->format($event->getThrowable()));
|
||||||
|
break;
|
||||||
|
case $event instanceof RetryHandle:
|
||||||
|
$this->logger->warning(sprintf('[%s] Retried %s.', $date, $jobClass));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
hyperf-skeleton/app/Model/Model.php
Normal file
18
hyperf-skeleton/app/Model/Model.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
|
||||||
|
*/
|
||||||
|
namespace App\Model;
|
||||||
|
|
||||||
|
use Hyperf\DbConnection\Model\Model as BaseModel;
|
||||||
|
|
||||||
|
abstract class Model extends BaseModel
|
||||||
|
{
|
||||||
|
}
|
22
hyperf-skeleton/app/Process/AsyncQueueConsumer.php
Normal file
22
hyperf-skeleton/app/Process/AsyncQueueConsumer.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\Process;
|
||||||
|
|
||||||
|
use Hyperf\AsyncQueue\Process\ConsumerProcess;
|
||||||
|
use Hyperf\Process\Annotation\Process;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Process
|
||||||
|
*/
|
||||||
|
class AsyncQueueConsumer extends ConsumerProcess
|
||||||
|
{
|
||||||
|
}
|
22
hyperf-skeleton/bin/hyperf.php
Normal file
22
hyperf-skeleton/bin/hyperf.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
ini_set('display_errors', 'on');
|
||||||
|
ini_set('display_startup_errors', 'on');
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
|
||||||
|
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
|
require BASE_PATH . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
})();
|
80
hyperf-skeleton/composer.json
Normal file
80
hyperf-skeleton/composer.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"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": ">=7.2",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-redis": "*",
|
||||||
|
"ext-swoole": ">=4.5",
|
||||||
|
"hyperf/cache": "~2.0.0",
|
||||||
|
"hyperf/command": "~2.0.0",
|
||||||
|
"hyperf/config": "~2.0.0",
|
||||||
|
"hyperf/db-connection": "~2.0.0",
|
||||||
|
"hyperf/framework": "~2.0.0",
|
||||||
|
"hyperf/guzzle": "~2.0.0",
|
||||||
|
"hyperf/http-server": "~2.0.0",
|
||||||
|
"hyperf/logger": "~2.0.0",
|
||||||
|
"hyperf/memory": "~2.0.0",
|
||||||
|
"hyperf/process": "~2.0.0",
|
||||||
|
"hyperf/redis": "~2.0.0",
|
||||||
|
"hyperf/json-rpc": "~2.0.0",
|
||||||
|
"hyperf/rpc": "~2.0.0",
|
||||||
|
"hyperf/rpc-client": "~2.0.0",
|
||||||
|
"hyperf/rpc-server": "~2.0.0",
|
||||||
|
"hyperf/constants": "~2.0.0",
|
||||||
|
"hyperf/async-queue": "~2.0.0",
|
||||||
|
"hyperf/amqp": "~2.0.0",
|
||||||
|
"hyperf/websocket-server": "^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"swoole/ide-helper": "^4.5",
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.14",
|
||||||
|
"mockery/mockery": "^1.0",
|
||||||
|
"phpstan/phpstan": "^0.12",
|
||||||
|
"hyperf/devtool": "~2.0.0",
|
||||||
|
"hyperf/testing": "~2.0.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,
|
||||||
|
"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 -c phpunit.xml --colors=always",
|
||||||
|
"cs-fix": "php-cs-fixer fix $1",
|
||||||
|
"analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config",
|
||||||
|
"start": "php ./bin/hyperf.php start"
|
||||||
|
}
|
||||||
|
}
|
38
hyperf-skeleton/config/autoload/amqp.php
Normal file
38
hyperf-skeleton/config/autoload/amqp.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
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
'default' => [
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 5672,
|
||||||
|
'user' => 'guest',
|
||||||
|
'password' => 'guest',
|
||||||
|
'vhost' => '/',
|
||||||
|
'pool' => [
|
||||||
|
'min_connections' => 1,
|
||||||
|
'max_connections' => 10,
|
||||||
|
'connect_timeout' => 10.0,
|
||||||
|
'wait_timeout' => 3.0,
|
||||||
|
'heartbeat' => -1,
|
||||||
|
],
|
||||||
|
'params' => [
|
||||||
|
'insist' => false,
|
||||||
|
'login_method' => 'AMQPLAIN',
|
||||||
|
'login_response' => null,
|
||||||
|
'locale' => 'en_US',
|
||||||
|
'connection_timeout' => 3.0,
|
||||||
|
'read_write_timeout' => 6.0,
|
||||||
|
'context' => null,
|
||||||
|
'keepalive' => false,
|
||||||
|
'heartbeat' => 3,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
21
hyperf-skeleton/config/autoload/annotations.php
Normal file
21
hyperf-skeleton/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
hyperf-skeleton/config/autoload/aspects.php
Normal file
13
hyperf-skeleton/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 [
|
||||||
|
];
|
21
hyperf-skeleton/config/autoload/async_queue.php
Normal file
21
hyperf-skeleton/config/autoload/async_queue.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 [
|
||||||
|
'default' => [
|
||||||
|
'driver' => Hyperf\AsyncQueue\Driver\RedisDriver::class,
|
||||||
|
'channel' => 'queue',
|
||||||
|
'timeout' => 2,
|
||||||
|
'retry_seconds' => 5,
|
||||||
|
'handle_timeout' => 10,
|
||||||
|
'processes' => 1,
|
||||||
|
],
|
||||||
|
];
|
18
hyperf-skeleton/config/autoload/cache.php
Normal file
18
hyperf-skeleton/config/autoload/cache.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
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
'default' => [
|
||||||
|
'driver' => Hyperf\Cache\Driver\RedisDriver::class,
|
||||||
|
'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
|
||||||
|
'prefix' => 'c:',
|
||||||
|
],
|
||||||
|
];
|
13
hyperf-skeleton/config/autoload/commands.php
Normal file
13
hyperf-skeleton/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 [
|
||||||
|
];
|
39
hyperf-skeleton/config/autoload/databases.php
Normal file
39
hyperf-skeleton/config/autoload/databases.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?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' => env('DB_DRIVER', 'mysql'),
|
||||||
|
'host' => env('DB_HOST', 'localhost'),
|
||||||
|
'database' => env('DB_DATABASE', 'hyperf'),
|
||||||
|
'port' => env('DB_PORT', 3306),
|
||||||
|
'username' => env('DB_USERNAME', 'root'),
|
||||||
|
'password' => env('DB_PASSWORD', ''),
|
||||||
|
'charset' => env('DB_CHARSET', 'utf8'),
|
||||||
|
'collation' => env('DB_COLLATION', 'utf8_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),
|
||||||
|
],
|
||||||
|
'commands' => [
|
||||||
|
'gen:model' => [
|
||||||
|
'path' => 'app/Model',
|
||||||
|
'force_casts' => true,
|
||||||
|
'inheritance' => 'Model',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
13
hyperf-skeleton/config/autoload/dependencies.php
Normal file
13
hyperf-skeleton/config/autoload/dependencies.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 [
|
||||||
|
];
|
44
hyperf-skeleton/config/autoload/devtool.php
Normal file
44
hyperf-skeleton/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
hyperf-skeleton/config/autoload/exceptions.php
Normal file
19
hyperf-skeleton/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,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
13
hyperf-skeleton/config/autoload/listeners.php
Normal file
13
hyperf-skeleton/config/autoload/listeners.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 [
|
||||||
|
];
|
30
hyperf-skeleton/config/autoload/logger.php
Normal file
30
hyperf-skeleton/config/autoload/logger.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
'default' => [
|
||||||
|
'handler' => [
|
||||||
|
'class' => Monolog\Handler\StreamHandler::class,
|
||||||
|
'constructor' => [
|
||||||
|
'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
|
||||||
|
'level' => Monolog\Logger::DEBUG,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'formatter' => [
|
||||||
|
'class' => Monolog\Formatter\LineFormatter::class,
|
||||||
|
'constructor' => [
|
||||||
|
'format' => null,
|
||||||
|
'dateFormat' => 'Y-m-d H:i:s',
|
||||||
|
'allowInlineLineBreaks' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
15
hyperf-skeleton/config/autoload/middlewares.php
Normal file
15
hyperf-skeleton/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
hyperf-skeleton/config/autoload/processes.php
Normal file
13
hyperf-skeleton/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 [
|
||||||
|
];
|
27
hyperf-skeleton/config/autoload/redis.php
Normal file
27
hyperf-skeleton/config/autoload/redis.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
|
||||||
|
*/
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
57
hyperf-skeleton/config/autoload/server.php
Normal file
57
hyperf-skeleton/config/autoload/server.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of Hyperf.
|
||||||
|
*
|
||||||
|
* @link https://www.hyperf.io
|
||||||
|
* @document https://hyperf.wiki
|
||||||
|
* @contact group@hyperf.io
|
||||||
|
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
use Hyperf\Server\Server;
|
||||||
|
use Hyperf\Server\SwooleEvent;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'mode' => SWOOLE_PROCESS,
|
||||||
|
'servers' => [
|
||||||
|
[
|
||||||
|
'name' => 'http',
|
||||||
|
'type' => Server::SERVER_HTTP,
|
||||||
|
'host' => '0.0.0.0',
|
||||||
|
'port' => 9501,
|
||||||
|
'sock_type' => SWOOLE_SOCK_TCP,
|
||||||
|
'callbacks' => [
|
||||||
|
SwooleEvent::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'ws',
|
||||||
|
'type' => Server::SERVER_WEBSOCKET,
|
||||||
|
'host' => '0.0.0.0',
|
||||||
|
'port' => 9502,
|
||||||
|
'sock_type' => SWOOLE_SOCK_TCP,
|
||||||
|
'callbacks' => [
|
||||||
|
SwooleEvent::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'],
|
||||||
|
SwooleEvent::ON_MESSAGE => [Hyperf\WebSocketServer\Server::class, 'onMessage'],
|
||||||
|
SwooleEvent::ON_CLOSE => [Hyperf\WebSocketServer\Server::class, 'onClose'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'settings' => [
|
||||||
|
'enable_coroutine' => true,
|
||||||
|
'worker_num' => swoole_cpu_num(),
|
||||||
|
'pid_file' => BASE_PATH . '/runtime/hyperf.pid',
|
||||||
|
'open_tcp_nodelay' => true,
|
||||||
|
'max_coroutine' => 100000,
|
||||||
|
'open_http2_protocol' => true,
|
||||||
|
'max_request' => 100000,
|
||||||
|
'socket_buffer_size' => 2 * 1024 * 1024,
|
||||||
|
'buffer_output_size' => 2 * 1024 * 1024,
|
||||||
|
],
|
||||||
|
'callbacks' => [
|
||||||
|
SwooleEvent::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
|
||||||
|
SwooleEvent::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
|
||||||
|
SwooleEvent::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
|
||||||
|
],
|
||||||
|
];
|
29
hyperf-skeleton/config/autoload/services.php
Normal file
29
hyperf-skeleton/config/autoload/services.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
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
'consumers' => [
|
||||||
|
[
|
||||||
|
// The service name, this name should as same as with the name of service provider.
|
||||||
|
'name' => 'YourServiceName',
|
||||||
|
// The service registry, if `nodes` is missing below, then you should provide this configs.
|
||||||
|
'registry' => [
|
||||||
|
'protocol' => 'consul',
|
||||||
|
'address' => 'Enter the address of service registry',
|
||||||
|
],
|
||||||
|
// If `registry` is missing, then you should provide the nodes configs.
|
||||||
|
'nodes' => [
|
||||||
|
// Provide the host and port of the service provider.
|
||||||
|
// ['host' => 'The host of the service provider', 'port' => 9502]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
31
hyperf-skeleton/config/config.php
Normal file
31
hyperf-skeleton/config/config.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
24
hyperf-skeleton/config/container.php
Normal file
24
hyperf-skeleton/config/container.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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\Di\Container;
|
||||||
|
use Hyperf\Di\Definition\DefinitionSourceFactory;
|
||||||
|
use Hyperf\Utils\ApplicationContext;
|
||||||
|
|
||||||
|
$container = new Container((new DefinitionSourceFactory(true))());
|
||||||
|
|
||||||
|
if (! $container instanceof \Psr\Container\ContainerInterface) {
|
||||||
|
throw new RuntimeException('The dependency injection container is invalid.');
|
||||||
|
}
|
||||||
|
return ApplicationContext::setContainer($container);
|
24
hyperf-skeleton/config/routes.php
Normal file
24
hyperf-skeleton/config/routes.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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 '';
|
||||||
|
});
|
||||||
|
|
||||||
|
Router::addServer('ws', function () {
|
||||||
|
Router::get('/', 'App\Controller\WebSocketController');
|
||||||
|
});
|
||||||
|
|
||||||
|
Router::addRoute(['GET', 'POST', 'HEAD'],'/main', 'App\Controller\MainController@index');
|
30
hyperf-skeleton/deploy.test.yml
Normal file
30
hyperf-skeleton/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=test"
|
||||||
|
ports:
|
||||||
|
- 9501:9501
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
delay: 5s
|
||||||
|
max_attempts: 5
|
||||||
|
update_config:
|
||||||
|
parallelism: 2
|
||||||
|
delay: 5s
|
||||||
|
order: start-first
|
||||||
|
networks:
|
||||||
|
- hyperf_net
|
||||||
|
configs:
|
||||||
|
- source: hyperf_v1.0
|
||||||
|
target: /opt/www/.env
|
||||||
|
configs:
|
||||||
|
hyperf_v1.0:
|
||||||
|
external: true
|
||||||
|
networks:
|
||||||
|
hyperf_net:
|
||||||
|
external: true
|
10
hyperf-skeleton/phpstan.neon
Normal file
10
hyperf-skeleton/phpstan.neon
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Magic behaviour with __get, __set, __call and __callStatic is not exactly static analyser-friendly :)
|
||||||
|
# Fortunately, You can ingore it by the following config.
|
||||||
|
#
|
||||||
|
# vendor/bin/phpstan analyse app --memory-limit 200M -l 0
|
||||||
|
#
|
||||||
|
parameters:
|
||||||
|
reportUnmatchedIgnoredErrors: false
|
||||||
|
ignoreErrors:
|
||||||
|
- '#Static call to instance method Hyperf\\HttpServer\\Router\\Router::[a-zA-Z0-9\\_]+\(\)#'
|
||||||
|
- '#Static call to instance method Hyperf\\DbConnection\\Db::[a-zA-Z0-9\\_]+\(\)#'
|
21
hyperf-skeleton/phpunit.xml
Normal file
21
hyperf-skeleton/phpunit.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
bootstrap="./test/bootstrap.php"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Tests">
|
||||||
|
<directory suffix="Test.php">./test</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||||
|
<directory suffix=".php">./app</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
27
hyperf-skeleton/test/Cases/ExampleTest.php
Normal file
27
hyperf-skeleton/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 HyperfTest\HttpTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class ExampleTest extends HttpTestCase
|
||||||
|
{
|
||||||
|
public function testExample()
|
||||||
|
{
|
||||||
|
$this->assertTrue(true);
|
||||||
|
$this->assertTrue(is_array($this->get('/')));
|
||||||
|
}
|
||||||
|
}
|
42
hyperf-skeleton/test/HttpTestCase.php
Normal file
42
hyperf-skeleton/test/HttpTestCase.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
|
||||||
|
*/
|
||||||
|
namespace HyperfTest;
|
||||||
|
|
||||||
|
use Hyperf\Testing\Client;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
29
hyperf-skeleton/test/bootstrap.php
Normal file
29
hyperf-skeleton/test/bootstrap.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
|
||||||
|
*/
|
||||||
|
ini_set('display_errors', 'on');
|
||||||
|
ini_set('display_startup_errors', 'on');
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
date_default_timezone_set('Asia/Shanghai');
|
||||||
|
|
||||||
|
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
|
||||||
|
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
|
||||||
|
|
||||||
|
Swoole\Runtime::enableCoroutine(true);
|
||||||
|
|
||||||
|
require BASE_PATH . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
Hyperf\Di\ClassLoader::init();
|
||||||
|
|
||||||
|
$container = require BASE_PATH . '/config/container.php';
|
||||||
|
|
||||||
|
$container->get(Hyperf\Contract\ApplicationInterface::class);
|
211
ws.html
Normal file
211
ws.html
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>简易WebSocket消息推送系统</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
*{
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
font-family: Helvetica;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
background: rgba( 71, 147, 227, 1);
|
||||||
|
}
|
||||||
|
h2{
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: white;
|
||||||
|
padding: 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Styles */
|
||||||
|
|
||||||
|
.table-wrapper{
|
||||||
|
margin: 10px 70px 70px;
|
||||||
|
box-shadow: 0px 35px 50px rgba( 0, 0, 0, 0.2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
.fl-table {
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
border: none;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fl-table td, .fl-table th {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fl-table td {
|
||||||
|
border-right: 1px solid #f8f8f8;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fl-table thead th {
|
||||||
|
color: #ffffff;
|
||||||
|
background: #4FC3A1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.fl-table thead th:nth-child(odd) {
|
||||||
|
color: #ffffff;
|
||||||
|
background: #324960;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fl-table tr:nth-child(even) {
|
||||||
|
background: #F8F8F8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.fl-table {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.table-wrapper:before{
|
||||||
|
content: "Scroll horizontally >";
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 11px;
|
||||||
|
color: white;
|
||||||
|
padding: 0 0 10px;
|
||||||
|
}
|
||||||
|
.fl-table thead, .fl-table tbody, .fl-table thead th {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.fl-table thead th:last-child{
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.fl-table thead {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.fl-table tbody {
|
||||||
|
width: auto;
|
||||||
|
position: relative;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.fl-table td, .fl-table th {
|
||||||
|
padding: 20px .625em .625em .625em;
|
||||||
|
height: 60px;
|
||||||
|
vertical-align: middle;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 120px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.fl-table thead th {
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #f7f7f9;
|
||||||
|
}
|
||||||
|
.fl-table tbody tr {
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
.fl-table tbody tr:nth-child(odd) {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
.fl-table tr:nth-child(even) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.fl-table tr td:nth-child(odd) {
|
||||||
|
background: #F8F8F8;
|
||||||
|
border-right: 1px solid #E6E4E4;
|
||||||
|
}
|
||||||
|
.fl-table tr td:nth-child(even) {
|
||||||
|
border-right: 1px solid #E6E4E4;
|
||||||
|
}
|
||||||
|
.fl-table tbody td {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>简易WebSocket消息推送系统</h2>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table class="fl-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>时间</th>
|
||||||
|
<th>姓名</th>
|
||||||
|
<th>学工号</th>
|
||||||
|
<th>地点</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="data">
|
||||||
|
<tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script src="http://libs.baidu.com/jquery/1.10.2/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
if ("WebSocket" in window) {
|
||||||
|
console.log("您的浏览器支持 WebSocket!");
|
||||||
|
var num = 0;
|
||||||
|
|
||||||
|
// 打开一个WebSocket
|
||||||
|
var ws = new WebSocket("ws://10.69.249.148:9502");
|
||||||
|
|
||||||
|
ws.onopen = function () {
|
||||||
|
// WebSocket 已连接上,使用 send() 方法发送数据
|
||||||
|
// alert("数据发送中...");
|
||||||
|
// ws.send("发送数据");
|
||||||
|
};
|
||||||
|
|
||||||
|
// 每隔30秒钟发送一次心跳,避免WebSocket连接因超时而自动断开
|
||||||
|
window.setInterval(function () {
|
||||||
|
var ping = {"type": "ping"};
|
||||||
|
ws.send(JSON.stringify(ping));
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
ws.onmessage = function (ret) {
|
||||||
|
console.log(ret.data);
|
||||||
|
var d = JSON.parse(ret.data);
|
||||||
|
if (d.code === 200) {
|
||||||
|
var data = d.data;
|
||||||
|
num++;
|
||||||
|
var str = `<tr>
|
||||||
|
<td>${data.recordOutTime}</td>
|
||||||
|
<td>${data.userOutName}</td>
|
||||||
|
<td>${data.userOutNum}</td>
|
||||||
|
<td>${data.doorOutName}</td>
|
||||||
|
</tr>`;
|
||||||
|
$("#data").prepend(str);
|
||||||
|
if (num > 6) {
|
||||||
|
num--;
|
||||||
|
$("#data tr:last").remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onerror = function (e) {
|
||||||
|
console.log(e);
|
||||||
|
alert(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function () {
|
||||||
|
// 关闭WebSocket
|
||||||
|
alert("连接已关闭...");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("您的浏览器不支持 WebSocket!");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user