├── .env.example ├── .github └── workflows │ ├── Dockerfile │ ├── build.yml │ └── release.yml ├── .gitignore ├── .gitlab-ci.yml ├── .php_cs ├── .phpstorm.meta.php ├── Dockerfile ├── LICENSE ├── README.md ├── app ├── Amqp │ ├── Consumer │ │ └── ChatConsumer.php │ └── Producer │ │ └── ChatProducer.php ├── Annotation │ └── Protocol.php ├── Cache │ ├── ApplyNumCache.php │ ├── FriendRemarkCache.php │ └── LastMsgCache.php ├── Command │ └── SocketIOClear.php ├── Component │ ├── ClientManager.php │ ├── Command │ │ ├── CliTable.php │ │ ├── CliTableManipulator.php │ │ └── Server.php │ ├── Hash.php │ ├── Jwt.php │ ├── Mail.php │ ├── MessageParser.php │ ├── Protocol.php │ ├── Proxy.php │ ├── ServerSender.php │ ├── Sms.php │ ├── SplitUpload.php │ └── UnreadTalk.php ├── Constants │ ├── Atomic.php │ ├── Consts.php │ ├── ErrorCode.php │ ├── Log.php │ ├── Message.php │ ├── Redis.php │ ├── User.php │ └── WsMessage.php ├── Controller │ ├── AbstractController.php │ ├── AuthController.php │ ├── DownloadController.php │ ├── EmoticonController.php │ ├── GroupController.php │ ├── HealthController.php │ ├── IndexController.php │ ├── SocketIOController.php │ ├── TalkController.php │ ├── TestOneController.php │ ├── UploadController.php │ └── UserController.php ├── Event │ └── LoginAfterEvent.php ├── Exception │ ├── BusinessException.php │ ├── Handler │ │ ├── AppExceptionHandler.php │ │ └── HandshakeExceptionHandler.php │ └── HandshakeException.php ├── Helper │ ├── ArrayHelper.php │ ├── DateHelper.php │ ├── DirectoryHelper.php │ ├── EncryptHelper.php │ ├── FileHelper.php │ ├── NumberHelper.php │ ├── RegularHelper.php │ ├── StringHelper.php │ └── ValidateHelper.php ├── JsonRpc │ ├── Client │ │ └── ProxyClient.php │ ├── Contract │ │ ├── InterfaceGroupService.php │ │ ├── InterfaceProxyService.php │ │ └── InterfaceUserService.php │ ├── GroupService.php │ ├── ProxyService.php │ └── UserService.php ├── Kernel │ ├── ClassMap │ │ └── Coroutine.php │ ├── Context │ │ └── Coroutine.php │ ├── Functions.php │ ├── Http │ │ └── Response.php │ ├── Lock │ │ └── RedisLock.php │ ├── Log │ │ ├── AppendRequestIdProcessor.php │ │ └── LoggerFactory.php │ ├── SocketIO │ │ ├── SocketIO.php │ │ └── SocketIOFactory.php │ └── WebSocket │ │ ├── Client.php │ │ ├── ClientConnection.php │ │ ├── ClientFactory.php │ │ ├── ClientProxy.php │ │ ├── Exception │ │ ├── InvalidWebSocketConnectionException.php │ │ └── InvalidWebSocketProxyException.php │ │ └── Pool │ │ ├── ClientPool.php │ │ └── PoolFactory.php ├── Listener │ ├── DbQueryExecutedListener.php │ ├── LoginAfterListener.php │ ├── OnShutdownListener.php │ ├── OnStartListener.php │ ├── OnWorkerStopListener.php │ └── QueueHandleListener.php ├── Middleware │ ├── CorsMiddleware.php │ ├── HttpAuthMiddleware.php │ └── SocketIOAuthMiddleware.php ├── Model │ ├── Article.php │ ├── ArticleAnnex.php │ ├── ArticleClass.php │ ├── ArticleDetail.php │ ├── ArticleTag.php │ ├── ChatRecords.php │ ├── ChatRecordsCode.php │ ├── ChatRecordsDelete.php │ ├── ChatRecordsFile.php │ ├── ChatRecordsForward.php │ ├── ChatRecordsInvite.php │ ├── Emoticon.php │ ├── EmoticonDetail.php │ ├── FileSplitUpload.php │ ├── Model.php │ ├── UserLoginLog.php │ ├── Users.php │ ├── UsersChatList.php │ ├── UsersEmoticon.php │ ├── UsersFriends.php │ ├── UsersFriendsApply.php │ ├── UsersGroup.php │ ├── UsersGroupMember.php │ └── UsersGroupNotice.php ├── Process │ └── AsyncQueueConsumer.php ├── Service │ ├── EmoticonService.php │ ├── GroupService.php │ ├── TalkService.php │ ├── Traits │ │ └── PagingTrait.php │ ├── UserFriendService.php │ └── UserService.php └── Task │ └── CloudTask.php ├── bin └── hyperf.php ├── composer.json ├── config ├── autoload │ ├── amqp.php │ ├── annotations.php │ ├── aspects.php │ ├── async_queue.php │ ├── cache.php │ ├── commands.php │ ├── consul.php │ ├── databases.php │ ├── db.php │ ├── dependencies.php │ ├── devtool.php │ ├── exceptions.php │ ├── file.php │ ├── jwt.php │ ├── listeners.php │ ├── logger.php │ ├── mail.php │ ├── middlewares.php │ ├── nsq.php │ ├── opentracing.php │ ├── processes.php │ ├── rate_limit.php │ ├── redis.php │ ├── server.php │ ├── services.php │ ├── view.php │ └── websocket_client.php ├── config.php ├── container.php └── routes.php ├── deploy.test.yml ├── hyperf-chat 架构.png ├── index.html ├── phpstan.neon ├── phpunit.xml ├── public ├── files │ └── .gitignore ├── media │ └── .gitignore └── tmp │ └── .gitignore ├── storage └── view │ └── emails │ └── verify-code.blade.php └── test ├── Cases └── ExampleTest.php ├── Http.php ├── HttpTestCase.php └── bootstrap.php /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=skeleton 2 | APP_ENV=dev 3 | 4 | DB_DRIVER=mysql 5 | DB_HOST=localhost 6 | DB_PORT=3306 7 | DB_DATABASE=hyperf 8 | DB_USERNAME=root 9 | DB_PASSWORD= 10 | DB_CHARSET=utf8mb4 11 | DB_COLLATION=utf8mb4_unicode_ci 12 | DB_PREFIX= 13 | 14 | REDIS_HOST=localhost 15 | REDIS_AUTH=(null) 16 | REDIS_PORT=6379 17 | REDIS_DB=0 18 | 19 | CLOUD_REDIS=default 20 | 21 | WEBSOCKET_SERVER_IPS = {"ws1":"127.0.0.1","ws2":"127.0.0.2"} 22 | AMQP_HOST=localhost 23 | NSQ_HOST=localhost 24 | CONSUL_HOST=localhost:8500 25 | NSQD_HOST=127.0.0.1:4151 26 | 27 | MAIL_HOST= 28 | MAIL_PORT= 29 | MAIL_USERNAME= 30 | MAIL_PASSWORD= 31 | MAIL_FROM= 32 | MAIL_NAME= 33 | 34 | IMAGE_URL=http://127.0.0.1:9500 35 | 36 | QINIU_ACCESS_KEY= 37 | QINIU_SECRET_KEY= 38 | QINIU_BUCKET= 39 | QINBIU_DOMAIN= 40 | 41 | -------------------------------------------------------------------------------- /.github/workflows/Dockerfile: -------------------------------------------------------------------------------- 1 | # Default Dockerfile 2 | # 3 | # @link https://www.hyperf.io 4 | # @document https://hyperf.wiki 5 | # @contact group@hyperf.io 6 | # @license https://github.com/hyperf/hyperf/blob/master/LICENSE 7 | 8 | FROM hyperf/hyperf:7.4-alpine-v3.11-swoole 9 | LABEL maintainer="Hyperf Developers " version="1.0" license="MIT" app.name="Hyperf" 10 | 11 | ## 12 | # ---------- env settings ---------- 13 | ## 14 | # --build-arg timezone=Asia/Shanghai 15 | ARG timezone 16 | 17 | ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \ 18 | APP_ENV=prod \ 19 | SCAN_CACHEABLE=(true) 20 | 21 | # update 22 | RUN set -ex \ 23 | # show php version and extensions 24 | && php -v \ 25 | && php -m \ 26 | && php --ri swoole \ 27 | # ---------- some config ---------- 28 | && cd /etc/php7 \ 29 | # - config PHP 30 | && { \ 31 | echo "upload_max_filesize=128M"; \ 32 | echo "post_max_size=128M"; \ 33 | echo "memory_limit=1G"; \ 34 | echo "date.timezone=${TIMEZONE}"; \ 35 | } | tee conf.d/99_overrides.ini \ 36 | # - config timezone 37 | && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ 38 | && echo "${TIMEZONE}" > /etc/timezone \ 39 | # ---------- clear works ---------- 40 | && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \ 41 | && echo -e "\033[42;37m Build Completed :).\033[0m\n" 42 | 43 | WORKDIR /opt/www 44 | 45 | # Composer Cache 46 | # COPY ./composer.* /opt/www/ 47 | # RUN composer install --no-dev --no-scripts 48 | 49 | COPY . /opt/www 50 | RUN composer install --no-dev -o && php bin/hyperf.php 51 | 52 | EXPOSE 9501 53 | 54 | ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"] 55 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v2 11 | - name: Build 12 | run: cp -rf .github/workflows/Dockerfile . && docker build -t hyperf . 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | # Sequence of patterns matched against refs/tags 4 | tags: 5 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 6 | 7 | name: Release 8 | 9 | jobs: 10 | release: 11 | name: Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | - name: Create Release 17 | id: create_release 18 | uses: actions/create-release@v1 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | with: 22 | tag_name: ${{ github.ref }} 23 | release_name: Release ${{ github.ref }} 24 | draft: false 25 | prerelease: false 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .buildpath 2 | .settings/ 3 | .project 4 | *.patch 5 | .idea/ 6 | .git/ 7 | runtime/ 8 | vendor/ 9 | .phpintel/ 10 | .env 11 | .DS_Store 12 | *.lock 13 | .phpunit* 14 | app/Controller/TestOneController.php 15 | app/Controller/IndexController.php 16 | .env.docker 17 | conf/ 18 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # usermod -aG docker gitlab-runner 2 | 3 | stages: 4 | - build 5 | - deploy 6 | 7 | variables: 8 | PROJECT_NAME: hyperf 9 | REGISTRY_URL: registry-docker.org 10 | 11 | build_test_docker: 12 | stage: build 13 | before_script: 14 | # - git submodule sync --recursive 15 | # - git submodule update --init --recursive 16 | script: 17 | - docker build . -t $PROJECT_NAME 18 | - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:test 19 | - docker push $REGISTRY_URL/$PROJECT_NAME:test 20 | only: 21 | - test 22 | tags: 23 | - builder 24 | 25 | deploy_test_docker: 26 | stage: deploy 27 | script: 28 | - docker stack deploy -c deploy.test.yml --with-registry-auth $PROJECT_NAME 29 | only: 30 | - test 31 | tags: 32 | - test 33 | 34 | build_docker: 35 | stage: build 36 | before_script: 37 | # - git submodule sync --recursive 38 | # - git submodule update --init --recursive 39 | script: 40 | - docker build . -t $PROJECT_NAME 41 | - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME 42 | - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:latest 43 | - docker push $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME 44 | - docker push $REGISTRY_URL/$PROJECT_NAME:latest 45 | only: 46 | - tags 47 | tags: 48 | - builder 49 | 50 | deploy_docker: 51 | stage: deploy 52 | script: 53 | - echo SUCCESS 54 | only: 55 | - tags 56 | tags: 57 | - builder 58 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | 11 | @link https://github.com/Hyperf-Glory/socket-io 12 | EOF; 13 | 14 | return PhpCsFixer\Config::create() 15 | ->setRiskyAllowed(true) 16 | ->setRules([ 17 | '@PSR2' => true, 18 | '@Symfony' => true, 19 | '@DoctrineAnnotation' => true, 20 | '@PhpCsFixer' => true, 21 | 'header_comment' => [ 22 | 'commentType' => 'PHPDoc', 23 | 'header' => $header, 24 | 'separate' => 'none', 25 | 'location' => 'after_declare_strict', 26 | ], 27 | 'no_superfluous_phpdoc_tags' => [ 28 | 'allow_mixed' => true, 29 | 'allow_unused_params' => true 30 | ], 31 | 'array_syntax' => [ 32 | 'syntax' => 'short' 33 | ], 34 | 'list_syntax' => [ 35 | 'syntax' => 'short' 36 | ], 37 | 'concat_space' => [ 38 | 'spacing' => 'one' 39 | ], 40 | 'blank_line_before_statement' => [ 41 | 'statements' => [ 42 | 'declare', 43 | ], 44 | ], 45 | 'general_phpdoc_annotation_remove' => [ 46 | 'annotations' => [ 47 | 'author' 48 | ], 49 | ], 50 | 'ordered_imports' => [ 51 | 'imports_order' => [ 52 | 'class', 'function', 'const', 53 | ], 54 | 'sort_algorithm' => 'alpha', 55 | ], 56 | 'single_line_comment_style' => [ 57 | 'comment_types' => [ 58 | ], 59 | ], 60 | 'yoda_style' => [ 61 | 'always_move_variable' => false, 62 | 'equal' => false, 63 | 'identical' => false, 64 | ], 65 | 'phpdoc_align' => [ 66 | 'align' => 'left', 67 | ], 68 | 'multiline_whitespace_before_semicolons' => [ 69 | 'strategy' => 'no_multi_line', 70 | ], 71 | 'constant_case' => [ 72 | 'case' => 'lower', 73 | ], 74 | 'class_attributes_separation' => true, 75 | 'combine_consecutive_unsets' => true, 76 | 'declare_strict_types' => true, 77 | 'linebreak_after_opening_tag' => true, 78 | 'lowercase_static_reference' => true, 79 | 'no_useless_else' => true, 80 | 'no_unused_imports' => true, 81 | 'not_operator_with_successor_space' => true, 82 | 'not_operator_with_space' => false, 83 | 'ordered_class_elements' => true, 84 | 'php_unit_strict' => false, 85 | 'phpdoc_separation' => false, 86 | 'single_quote' => true, 87 | 'standardize_not_equals' => true, 88 | 'multiline_comment_opening_closing' => true, 89 | ]) 90 | ->setFinder( 91 | PhpCsFixer\Finder::create() 92 | ->exclude('public') 93 | ->exclude('runtime') 94 | ->exclude('vendor') 95 | ->in(__DIR__) 96 | ) 97 | ->setUsingCache(false); 98 | -------------------------------------------------------------------------------- /.phpstorm.meta.php: -------------------------------------------------------------------------------- 1 | " version="1.0" license="MIT" app.name="Hyperf" 10 | 11 | ## 12 | # ---------- env settings ---------- 13 | ## 14 | # --build-arg timezone=Asia/Shanghai 15 | ARG timezone 16 | 17 | ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \ 18 | APP_ENV=dev \ 19 | SCAN_CACHEABLE=(true) 20 | 21 | # update 22 | RUN set -ex \ 23 | # install composer 24 | && cd /tmp \ 25 | # show php version and extensions 26 | && php -v \ 27 | && php -m \ 28 | && php --ri swoole \ 29 | && php --ri amqp \ 30 | && php --ri redis \ 31 | # ---------- some config ---------- 32 | && cd /etc/php7 \ 33 | # - config PHP 34 | && { \ 35 | echo "upload_max_filesize=128M"; \ 36 | echo "post_max_size=128M"; \ 37 | echo "memory_limit=1G"; \ 38 | echo "date.timezone=${TIMEZONE}"; \ 39 | } | tee conf.d/99_overrides.ini \ 40 | # - config timezone 41 | && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ 42 | && echo "${TIMEZONE}" > /etc/timezone \ 43 | # ---------- clear works ---------- 44 | && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \ 45 | && echo -e "\033[42;37m Build Completed :).\033[0m\n" 46 | 47 | WORKDIR /opt/www 48 | 49 | # Composer Cache 50 | # COPY ./composer.* /opt/www/ 51 | # RUN composer install --no-dev --no-scripts 52 | 53 | COPY . /opt/www 54 | COPY ./.env.example /opt/www/.env 55 | 56 | RUN composer install --no-dev -o && php bin/hyperf.php 57 | 58 | EXPOSE 9500 59 | EXPOSE 9502 60 | EXPOSE 9504 61 | 62 | ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"] 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Hyperf-Glory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/Amqp/Consumer/ChatConsumer.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Amqp\Consumer; 15 | 16 | use Hyperf\Amqp\Annotation\Consumer; 17 | use Hyperf\Amqp\Message\ConsumerMessage; 18 | use Hyperf\Amqp\Result; 19 | use Hyperf\Utils\Coroutine; 20 | use PhpAmqpLib\Message\AMQPMessage; 21 | 22 | /** 23 | * @Consumer(exchange="hyperf", routingKey="hyperf", queue="hyperf", name="ChatConsumer", nums="1") 24 | */ 25 | class ChatConsumer extends ConsumerMessage 26 | { 27 | public function consumeMessage($data, AMQPMessage $message): string 28 | { 29 | Coroutine::create(function () use ($data) { 30 | [$packet, $opts] = unserialize($data->payload); 31 | $this->doBroadcast($packet, $opts); 32 | }); 33 | return Result::ACK; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Amqp/Producer/ChatProducer.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Amqp\Producer; 15 | 16 | use Hyperf\Amqp\Annotation\Producer; 17 | use Hyperf\Amqp\Message\ProducerMessage; 18 | 19 | /** 20 | * @Producer(exchange="hyperf", routingKey="hyperf") 21 | */ 22 | class ChatProducer extends ProducerMessage 23 | { 24 | public function __construct(string $data) 25 | { 26 | $this->payload = $data; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Annotation/Protocol.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Annotation; 15 | 16 | use Hyperf\Di\Annotation\AbstractAnnotation; 17 | 18 | /** 19 | * @Annotation 20 | * @Target("ALL") 21 | */ 22 | class Protocol extends AbstractAnnotation 23 | { 24 | public $cmd = ''; 25 | 26 | public $data = ''; 27 | 28 | public $ext = ''; 29 | } 30 | -------------------------------------------------------------------------------- /app/Cache/ApplyNumCache.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Cache; 15 | 16 | use Hyperf\Redis\RedisFactory; 17 | use Hyperf\Redis\RedisProxy; 18 | 19 | /** 20 | * Class ApplyNumCache. 21 | */ 22 | class ApplyNumCache 23 | { 24 | public const KEY = 'friend:apply:unread:num'; 25 | 26 | /** 27 | * 获取好友未读申请数. 28 | */ 29 | public static function get(int $uid, ?RedisProxy $redis = null) 30 | { 31 | if (is_null($redis)) { 32 | $redis = self::redis(); 33 | } 34 | return $redis->hGet(self::KEY, (string) $uid); 35 | } 36 | 37 | /** 38 | * 设置未读好友申请数(自增加1). 39 | */ 40 | public static function setInc(int $uid, ?RedisProxy $redis = null): int 41 | { 42 | if (is_null($redis)) { 43 | $redis = self::redis(); 44 | } 45 | 46 | return $redis->hIncrBy(self::KEY, (string) $uid, 1); 47 | } 48 | 49 | /** 50 | * 删除好友申请未读数. 51 | */ 52 | public static function del(int $uid, ?RedisProxy $redis = null): void 53 | { 54 | if (is_null($redis)) { 55 | $redis = self::redis(); 56 | } 57 | $redis->hDel(self::KEY, (string) $uid); 58 | } 59 | 60 | /** 61 | * 获取Redis连接. 62 | * 63 | * @return RedisProxy| 64 | */ 65 | private static function redis(): RedisProxy 66 | { 67 | return di(RedisFactory::class)->get(env('CLOUD_REDIS')); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/Cache/FriendRemarkCache.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Cache; 15 | 16 | use Hyperf\Redis\RedisFactory; 17 | use Hyperf\Redis\RedisProxy; 18 | 19 | /** 20 | * Class FriendRemarkCache. 21 | */ 22 | class FriendRemarkCache 23 | { 24 | public const KEY = 'hash:user:friend:remark:cache'; 25 | 26 | /** 27 | * 设置好友备注缓存. 28 | * 29 | * @param string $remark 好友备注 30 | */ 31 | public static function set(int $uid, int $fid, string $remark, ?RedisProxy $redis = null) 32 | { 33 | if (is_null($redis)) { 34 | $redis = self::redis(); 35 | } 36 | $redis->hSet(self::KEY, "{$uid}_{$fid}", $remark); 37 | } 38 | 39 | /** 40 | * 获取好友备注. 41 | */ 42 | public static function get(int $uid, int $fid, ?RedisProxy $redis = null): string 43 | { 44 | if (is_null($redis)) { 45 | $redis = self::redis(); 46 | } 47 | return $redis->hGet(self::KEY, "{$uid}_{$fid}") ?: ''; 48 | } 49 | 50 | /** 51 | * 获取Redis连接. 52 | */ 53 | private static function redis(): RedisProxy 54 | { 55 | return di(RedisFactory::class)->get(env('CLOUD_REDIS')); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/Cache/LastMsgCache.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Cache; 15 | 16 | use App\Component\MessageParser; 17 | use Hyperf\Redis\RedisFactory; 18 | use Hyperf\Redis\RedisProxy; 19 | 20 | /** 21 | * Class LastMsgCache. 22 | */ 23 | class LastMsgCache 24 | { 25 | /** 26 | * 设置好友之间或群聊中发送的最后一条消息缓存. 27 | * 28 | * @param array $message 消息内容 29 | * @param int $receive 接收者 30 | * @param int $sender 发送者(注:若聊天消息类型为群聊消息 $sender 应设置为0) 31 | */ 32 | public static function set(array $message, int $receive, $sender = 0, ?RedisProxy $redis = null) 33 | { 34 | if (is_null($redis)) { 35 | $redis = self::redis(); 36 | } 37 | 38 | $redis->hSet(self::_name($sender), self::_key($receive, $sender), MessageParser::serialize($message)); 39 | } 40 | 41 | /** 42 | * 获取好友之间或群聊中发送的最后一条消息缓存. 43 | * 44 | * @param int $receive 接收者 45 | * @param int $sender 发送者(注:若聊天消息类型为群聊消息 $sender 应设置为0) 46 | * 47 | * @return mixed 48 | */ 49 | public static function get(int $receive, $sender = 0, ?RedisProxy $redis = null) 50 | { 51 | if (is_null($redis)) { 52 | $redis = self::redis(); 53 | } 54 | $data = $redis->hGet(self::_name($sender), self::_key($receive, $sender)); 55 | 56 | return $data ? MessageParser::unserialize($data) : null; 57 | } 58 | 59 | /** 60 | * 用户聊天或群聊的最后一条消息hash存储的hash名. 61 | * 62 | * @param int $sender 63 | */ 64 | private static function _name($sender = 0): string 65 | { 66 | return $sender === 0 ? 'groups:chat:last.msg' : 'friends:chat:last:msg'; 67 | } 68 | 69 | /** 70 | * 获取hash key. 71 | * 72 | * @param int $receive 接收者 73 | * @param int $sender 发送者 74 | * 75 | * @return string 76 | */ 77 | private static function _key(int $receive, int $sender) 78 | { 79 | return $receive < $sender ? "{$receive}_{$sender}" : "{$sender}_{$receive}"; 80 | } 81 | 82 | /** 83 | * 获取Redis连接. 84 | * 85 | * @return RedisProxy 86 | */ 87 | private static function redis() 88 | { 89 | return di(RedisFactory::class)->get(env('CLOUD_REDIS')); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/Command/SocketIOClear.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Command; 15 | 16 | use Hyperf\Command\Command; 17 | use Hyperf\Redis\Redis; 18 | use Hyperf\Redis\RedisFactory; 19 | use Symfony\Component\Console\Input\InputArgument; 20 | 21 | class SocketIOClear extends Command 22 | { 23 | protected $redisPrefix = 'ws'; 24 | 25 | protected $connection = 'default'; 26 | 27 | /** 28 | * @var \Redis|Redis 29 | */ 30 | private $redis; 31 | 32 | public function __construct(RedisFactory $factory) 33 | { 34 | parent::__construct('socketio-self:clear'); 35 | $this->redis = $factory->get(env('CLOUD_REDIS', 'default')); 36 | } 37 | 38 | public function handle(): void 39 | { 40 | $nsp = $this->input->getArgument('namespace') ?? '/'; 41 | $serverId = $this->input->getArgument('serverId'); 42 | $prefix = implode(':', [ 43 | $this->redisPrefix, 44 | $nsp, 45 | 'fds', 46 | $serverId, 47 | ]); 48 | $iterator = null; 49 | while (false !== ($keys = $this->redis->scan($iterator, "{$prefix}*"))) { 50 | $this->redis->del($keys); 51 | } 52 | } 53 | 54 | protected function getArguments(): array 55 | { 56 | return [ 57 | ['namespace', InputArgument::REQUIRED, 'The namespace to be cleaned up.'], 58 | ['serverId', InputArgument::REQUIRED, 'The self SokcetIO Server to be cleaned up.'], 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Component/ClientManager.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | use App\Helper\ArrayHelper; 17 | use Hyperf\Redis\RedisProxy; 18 | 19 | class ClientManager 20 | { 21 | public const HASH_UID_TO_SID_PREFIX = 'hash.socket_user_fd'; 22 | 23 | public const HASH_FD_TO_UID_PREFIX = 'hash.socket_fd_user'; 24 | 25 | public const ZSET_IP_TO_UID = 'zset.ip_to_uid_bind'; 26 | 27 | /** 28 | *存储fd,ip,uid. 29 | */ 30 | public static function put(RedisProxy $redis, string $uid, int $fd) 31 | { 32 | //bind key to fd 33 | $redis->hSet(self::HASH_UID_TO_SID_PREFIX, $uid, $fd); 34 | $redis->hSet(self::HASH_FD_TO_UID_PREFIX, $fd, $uid); 35 | } 36 | 37 | /** 38 | * 删除对应关系. 39 | */ 40 | public static function del(RedisProxy $redis, string $uid, int $fd = null) 41 | { 42 | //del key to fd 43 | $redis->hDel(self::HASH_UID_TO_SID_PREFIX, $uid); 44 | $redis->hDel(self::HASH_FD_TO_UID_PREFIX, $fd); 45 | } 46 | 47 | public static function disconnect(RedisProxy $redis, int $fd) 48 | { 49 | $uid = $redis->hGet(self::HASH_FD_TO_UID_PREFIX, $fd); 50 | if (empty($uid)) { 51 | return; 52 | } 53 | self::del($redis, $uid, $fd); 54 | } 55 | 56 | /** 57 | * @return null|int 58 | */ 59 | public static function fd(\Redis $redis, string $uid) 60 | { 61 | return (int) $redis->hGet(self::HASH_UID_TO_SID_PREFIX, $uid) ?? null; 62 | } 63 | 64 | /** 65 | * @return array 66 | */ 67 | public static function fds(RedisProxy $redis, array $uids = []) 68 | { 69 | if (empty($uids)) { 70 | return []; 71 | } 72 | return ArrayHelper::multiArrayValues($redis->hMGet(self::HASH_UID_TO_SID_PREFIX, $uids) ?? []); 73 | } 74 | 75 | /** 76 | * @return null|string 77 | */ 78 | public static function key(RedisProxy $redis, int $fd) 79 | { 80 | return $redis->hGet(self::HASH_FD_TO_UID_PREFIX, $fd) ?? null; 81 | } 82 | 83 | /** 84 | * @return array|void 85 | */ 86 | public static function getIpUid(RedisProxy $redis, string $ip = null) 87 | { 88 | if (empty($ip)) { 89 | return; 90 | } 91 | return $redis->sMembers(sprintf('%s.%s', self::ZSET_IP_TO_UID, $ip)); 92 | } 93 | 94 | /** 95 | * @return false|string 96 | */ 97 | public static function isOnline(RedisProxy $redis, int $uid) 98 | { 99 | return $redis->hGet(self::HASH_UID_TO_SID_PREFIX, $uid) ?? false; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/Component/Command/Server.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component\Command; 15 | 16 | class Server 17 | { 18 | public const LOGO = <<<'EOF' 19 | _______ _______ _______ _______ 20 | |\ /||\ /|( ____ )( ____ \( ____ )( ____ \ | 21 | | ) ( |( \ / )| ( )|| ( \/| ( )|| ( \/ | 22 | | (___) | \ (_) / | (____)|| (__ | (____)|| (__ | 23 | | ___ | \ / | _____)| __) | __)| __) | 24 | | ( ) | ) ( | ( | ( | (\ ( | ( | 25 | | ) ( | | | | ) | (____/\| ) \ \__| ) | 26 | |/ \| \_/ |/ (_______/|/ \__/|/ | 27 | 28 | _______ _______ _________ | 29 | ( ____ \|\ /|( ___ )\__ __/ | 30 | | ( \/| ) ( || ( ) | ) ( | 31 | | | | (___) || (___) | | | | 32 | | | | ___ || ___ | | | | 33 | | | | ( ) || ( ) | | | | 34 | | (____/\| ) ( || ) ( | | | | 35 | (_______/|/ \||/ \| )_( 36 | EOF; 37 | } 38 | -------------------------------------------------------------------------------- /app/Component/Hash.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | use Crypto\HashException; 17 | 18 | class Hash 19 | { 20 | /** 21 | * Make a hash from the given plain data. 22 | */ 23 | public static function make(string $plain): string 24 | { 25 | $result = password_hash($plain, PASSWORD_BCRYPT); 26 | if ($result === false) { 27 | throw new HashException(); 28 | } 29 | 30 | return $result; 31 | } 32 | 33 | /** 34 | * Verify the given plain with the given hashed value. 35 | */ 36 | public static function verify(string $plain, string $hashed): bool 37 | { 38 | return password_verify($plain, $hashed); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Component/Jwt.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | /** 17 | * Class Jwt. 18 | * @TODO 待封装 19 | */ 20 | class Jwt 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /app/Component/MessageParser.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | use App\Model\Users; 17 | use Hyperf\Utils\Codec\Json; 18 | 19 | class MessageParser 20 | { 21 | public static function decode(string $data): array 22 | { 23 | $data = sprintf('%s%s%s', pack('N', strlen($data)), $data, "\r\n"); 24 | $strlen = strlen($data); 25 | return swoole_substr_json_decode($data, 4, $strlen - 6, true); 26 | } 27 | 28 | public static function encode(array $data): string 29 | { 30 | return Json::encode($data); 31 | } 32 | 33 | /** 34 | * @param $data 35 | * 36 | * @return string 37 | */ 38 | public static function serialize($data) 39 | { 40 | return serialize($data); 41 | } 42 | 43 | /** 44 | * @return mixed 45 | */ 46 | public static function unserialize(string $data) 47 | { 48 | $str = pack('N', strlen($data)) . $data . "\r\n"; 49 | $strlen = strlen($data); 50 | return swoole_substr_unserialize($str, 4, $strlen); 51 | } 52 | 53 | /** 54 | * 格式化对话的消息体. 55 | * 56 | * @param array $data 对话的消息 57 | */ 58 | public static function formatTalkMsg(array $data): array 59 | { 60 | // 缓存优化 61 | if (! isset($data['nickname'],$data['avatar']) || empty($data['nickname']) || empty($data['avatar'])) { 62 | if (isset($data['user_id']) && ! empty($data['user_id'])) { 63 | /** 64 | * @var Users $info 65 | */ 66 | $info = Users::where('id', $data['user_id'])->first(['nickname', 'avatar']); 67 | if ($info) { 68 | $data['nickname'] = $info->nickname; 69 | $data['avatar'] = $info->avatar; 70 | } 71 | } 72 | } 73 | 74 | $arr = [ 75 | 'id' => 0, 76 | 'source' => 1, 77 | 'msg_type' => 1, 78 | 'user_id' => 0, 79 | 'receive_id' => 0, 80 | 'content' => '', 81 | 'is_revoke' => 0, 82 | 83 | // 发送消息人的信息 84 | 'nickname' => '', 85 | 'avatar' => '', 86 | 87 | // 不同的消息类型 88 | 'file' => [], 89 | 'code_block' => [], 90 | 'forward' => [], 91 | 'invite' => [], 92 | 93 | 'created_at' => '', 94 | ]; 95 | 96 | return array_merge($arr, array_intersect_key($data, $arr)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/Component/Protocol.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | class Protocol 17 | { 18 | /** 19 | * @var array 20 | */ 21 | protected $ext = []; 22 | 23 | /** 24 | * Raw data. 25 | * 26 | * @var array 27 | */ 28 | protected $data = []; 29 | 30 | /** 31 | * @var int 32 | */ 33 | protected $fd = 0; 34 | 35 | /** 36 | * @var float 37 | */ 38 | protected $requestTime = 0; 39 | 40 | public function __construct(array $data = [], array $ext = [], int $fd = 0, float $requestTime = 0) 41 | { 42 | $this->data = $data; 43 | $this->ext = $ext; 44 | $this->fd = $fd; 45 | $this->requestTime = $requestTime; 46 | } 47 | 48 | public function getData(): array 49 | { 50 | return $this->data; 51 | } 52 | 53 | public function getExt(): array 54 | { 55 | return $this->ext; 56 | } 57 | 58 | public function getFd(): int 59 | { 60 | return $this->fd; 61 | } 62 | 63 | public function getRequestTime(): float 64 | { 65 | return $this->requestTime; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Component/ServerSender.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | use Hyperf\Utils\Coroutine; 17 | use Hyperf\WebSocketServer\Sender; 18 | use Swoole\Server; 19 | 20 | class ServerSender 21 | { 22 | /** 23 | * @param $data 24 | */ 25 | public function sendToAll($data, array $fds = []): void 26 | { 27 | foreach ($fds as $fd) { 28 | $this->push($fd, $data); 29 | } 30 | } 31 | 32 | /** 33 | * @param $data 34 | * 35 | * @return mixed 36 | */ 37 | public function push($data, int $fd) 38 | { 39 | return di(Sender::class)->push($fd, $data); 40 | } 41 | 42 | /** 43 | * Disconnect for client, will trigger onClose. 44 | * 45 | * @return bool|mixed 46 | */ 47 | public function disconnect(int $fd, int $code = 0, string $reason = '') 48 | { 49 | return di(Sender::class)->disconnect($fd, $code, $reason); 50 | } 51 | 52 | public function close(int $fd): void 53 | { 54 | if (Coroutine::inCoroutine()) { 55 | Coroutine::create(function () use ($fd) { 56 | self::disconnect($fd); 57 | }); 58 | } 59 | di(Server::class)->close($fd); 60 | } 61 | 62 | /** 63 | * @return bool 64 | */ 65 | public function isWsClient(int $fd) 66 | { 67 | $swooleServer = di(\Hyperf\Server\Server::class)->getServer(); 68 | $client = $swooleServer->getClientInfo($fd); 69 | if (isset($client['websocket_status']) && $client['websocket_status'] === WEBSOCKET_STATUS_FRAME) { 70 | return true; 71 | } 72 | return false; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/Component/UnreadTalk.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Component; 15 | 16 | use Hyperf\Redis\RedisFactory; 17 | use Hyperf\Redis\RedisProxy; 18 | 19 | /** 20 | * 好友未读消息服务 21 | * 22 | * Class UnreadTalkService 23 | */ 24 | class UnreadTalk 25 | { 26 | const KEY = 'hash:unread_talk'; 27 | 28 | /** 29 | * 设置用户未读消息(自增加1). 30 | * 31 | * @param int $uid 用户ID 32 | * @param int $fid 好友ID 33 | */ 34 | public function setInc(int $uid, int $fid, ?RedisProxy $redis = null): bool 35 | { 36 | if (is_null($redis)) { 37 | $redis = $this->redis(); 38 | } 39 | $num = $this->get($uid, $uid) + 1; 40 | 41 | return (bool) $redis->hset($this->_key($uid), (string) $fid, $num); 42 | } 43 | 44 | /** 45 | * 获取用户指定好友的未读消息数. 46 | * 47 | * @param int $uid 用户ID 48 | * @param int $fid 好友ID 49 | */ 50 | public function get(int $uid, int $fid, ?RedisProxy $redis = null): int 51 | { 52 | if (is_null($redis)) { 53 | $redis = $this->redis(); 54 | } 55 | 56 | return (int) $redis->hget($this->_key($uid), (string) $fid); 57 | } 58 | 59 | /** 60 | * 获取用户未读消息列表. 61 | * 62 | * @param int $uid 用户ID 63 | * 64 | * @return mixed 65 | */ 66 | public function getAll(int $uid, ?RedisProxy $redis = null) 67 | { 68 | if (is_null($redis)) { 69 | $redis = $this->redis(); 70 | } 71 | 72 | return $redis->hgetall($this->_key($uid)); 73 | } 74 | 75 | /** 76 | * 清除用户指定好友的未读消息. 77 | * 78 | * @param int $uid 用户ID 79 | * @param int $fid 好友ID 80 | */ 81 | public function del(int $uid, int $fid, ?RedisProxy $redis = null): bool 82 | { 83 | if (is_null($redis)) { 84 | $redis = $this->redis(); 85 | } 86 | return (bool) $redis->hdel($this->_key($uid), (string) $fid); 87 | } 88 | 89 | /** 90 | * 清除用户所有好友未读数. 91 | */ 92 | public function delAll(int $uid, ?RedisProxy $redis = null): bool 93 | { 94 | if (is_null($redis)) { 95 | $redis = $this->redis(); 96 | } 97 | 98 | return (bool) $redis->del($this->_key($uid)); 99 | } 100 | 101 | /** 102 | * 获取缓存key. 103 | * 104 | * @param int $uid 用户ID 105 | */ 106 | private function _key(int $uid, ?RedisProxy $redis = null): string 107 | { 108 | return self::KEY . ":{$uid}"; 109 | } 110 | 111 | /** 112 | * 获取Redis连接. 113 | */ 114 | private function redis(): RedisProxy 115 | { 116 | return di(RedisFactory::class)->get(env('CLOUD_REDIS')); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/Constants/Atomic.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | use Hyperf\Constants\AbstractConstants; 17 | use Hyperf\Constants\Annotation\Constants; 18 | 19 | /** 20 | * Class Atomic. 21 | * @Constants 22 | */ 23 | class Atomic extends AbstractConstants 24 | { 25 | const NAME = 'atomic'; 26 | } 27 | -------------------------------------------------------------------------------- /app/Constants/Consts.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | class Consts 17 | { 18 | //整数相关 19 | const INT_3B_MAX = 255; //3位最大整数值,tinyint(3) 20 | 21 | const INT_5B_MAX = 65535; //5位最大整数值,smallint(5) 22 | 23 | const INT_8B_MAX = 16777215; //8位最大整数值,mediumint(8) 24 | 25 | const INT_10B_MAX = 4294967295; //10位最大整数值,int(10) 26 | 27 | //有效期时间 28 | const TTL_LONG = 0; //永久 29 | 30 | const TTL_DEFAULT = 1800; //默认缓存时间30分钟 31 | 32 | const TTL_ONE_MONTH = 2592000; //缓存时间1月 33 | 34 | const TTL_HALF_MONTH = 1296000; //缓存时间0.5月 35 | 36 | const TTL_ONE_WEEK = 604800; //缓存时间1周 37 | 38 | const TTL_ONE_DAY = 86400; //缓存时间1天 39 | 40 | const TTL_HALF_DAY = 43200; //缓存时间0.5天 41 | 42 | const TTL_ONE_HOUR = 3600; //缓存时间1小时 43 | 44 | const TTL_HALF_HOUR = 1800; //缓存时间半小时 45 | 46 | const TTL_FIF_MINUTE = 900; //缓存时间15分钟 47 | 48 | const TTL_TEN_MINUTE = 600; //缓存时间10分钟 49 | 50 | const TTL_FIV_MINUTE = 300; //缓存时间5分钟 51 | 52 | const TTL_TWO_MINUTE = 120; //缓存时间2分钟 53 | 54 | const TTL_ONE_MINUTE = 60; //缓存时间1分钟 55 | 56 | const UNKNOWN = 'Unknown'; //未知字符串 57 | 58 | const DELIMITER = '$@#KSYS#@$'; //本库自定义分隔符 59 | 60 | const PAAMAYIM_NEKUDOTAYIM = '::'; //范围解析操作符-双冒号 61 | 62 | const DYNAMIC_KEY_LEN = 8; //authcode动态密钥长度,须<32 63 | } 64 | -------------------------------------------------------------------------------- /app/Constants/Log.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | use Hyperf\Constants\AbstractConstants; 17 | use Hyperf\Constants\Annotation\Constants; 18 | 19 | /** 20 | * Class Atomic. 21 | * @Constants 22 | */ 23 | class Log extends AbstractConstants 24 | { 25 | const CONTEXT_KEY = 'log.context.key'; 26 | } 27 | -------------------------------------------------------------------------------- /app/Constants/Message.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | use Hyperf\Constants\Annotation\Constants; 17 | 18 | /** 19 | * Class Message. 20 | * @Constants 21 | */ 22 | class Message 23 | { 24 | const TITLE = <<<'TITLE' 25 | 26 | ___ ___ ___ ___ 27 | / /\ __ ___ / /\ / /\ ___ ___ / /\ 28 | / /:/ | |\ / /\ / /::\ / /::\ / /\ /__/\ / /::| 29 | / /:/ | |:| / /::\ / /:/\:\ / /:/\:\ / /::\ \__\:\ / /:|:| 30 | / /::\ ___ | |:| / /:/\:\ / /::\ \:\ / /::\ \:\ / /:/\:\ / /::\ / /:/|:|__ 31 | /__/:/\:\ /\ |__|:|__ / /::\ \:\ /__/:/\:\ \:\ /__/:/\:\_\:\ / /::\ \:\ __/ /:/\/ /__/:/_|::::\ 32 | \__\/ \:\/:/ / /::::\ /__/:/\:\_\:\ \ \:\ \:\_\/ \__\/~|::\/:/ /__/:/\:\ \:\ /__/\/:/~~ \__\/ /~~/:/ 33 | \__\::/ / /:/~~~~ \__\/ \:\/:/ \ \:\ \:\ | |:|::/ \__\/ \:\_\/ \ \::/ / /:/ 34 | / /:/ /__/:/ \ \::/ \ \:\_\/ | |:|\/ \ \:\ \ \:\ / /:/ 35 | /__/:/ \__\/ \__\/ \ \:\ |__|:|~ \__\/ \__\/ /__/:/ 36 | \__\/ \__\/ \__\| \__\/ 37 | 38 | TITLE; 39 | } 40 | -------------------------------------------------------------------------------- /app/Constants/Redis.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | use Hyperf\Constants\AbstractConstants; 17 | use Hyperf\Constants\Annotation\Constants; 18 | 19 | /** 20 | * Class MemoryTable. 21 | * @Constants 22 | */ 23 | class Redis extends AbstractConstants 24 | { 25 | const FD_TO_USER = 'fdToUser'; 26 | 27 | const USER_TO_FD = 'userToFd'; 28 | 29 | const SUBJECT_USER_TO_FD = 'subjectUserToFd'; 30 | 31 | const SUBJECT_FD_TO_USER = 'subjectFdToUser'; 32 | 33 | const SUBJECT_TO_USER = 'subjectToUser'; 34 | 35 | const USER_TO_SUBJECT = 'userToSubject'; 36 | } 37 | -------------------------------------------------------------------------------- /app/Constants/User.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | use Hyperf\Constants\AbstractConstants; 17 | use Hyperf\Constants\Annotation\Constants; 18 | 19 | /** 20 | * Class User. 21 | * @Constants 22 | */ 23 | class User extends AbstractConstants 24 | { 25 | const REGISTER = 'user_register'; 26 | 27 | const CHANGE_MOBILE = 'change_mobile'; 28 | 29 | const FORGET_PASSWORD = 'forget_password'; 30 | 31 | const CHANGE_PASSWORD = 'change_password'; 32 | } 33 | -------------------------------------------------------------------------------- /app/Constants/WsMessage.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Constants; 15 | 16 | use Hyperf\Constants\AbstractConstants; 17 | use Hyperf\Constants\Annotation\Constants; 18 | 19 | /** 20 | * Class WsMessage. 21 | * @Constants 22 | */ 23 | class WsMessage extends AbstractConstants 24 | { 25 | const WS_MESSAGE_CMD_EVENT = 'system.event'; 26 | 27 | const WS_MESSAGE_CMD_ERROR = 'system.error'; 28 | 29 | const EVENT_USER_STATUS = 'setUserStatus'; 30 | 31 | const EVENT_GET_MESSAGE = 'getMessage'; 32 | 33 | const EVENT_GET_UNREAD_APPLICATION_COUNT = 'getUnreadApplicationCount'; 34 | 35 | const EVENT_FRIEND_AGREE_APPLY = 'friendAgreeApply'; 36 | 37 | const EVENT_GROUP_AGREE_APPLY = 'groupAgreeApply'; 38 | 39 | const EVENT_FRIEND_VIDEO_ROOM = 'friendVideoRoom'; 40 | } 41 | -------------------------------------------------------------------------------- /app/Controller/AbstractController.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Controller; 15 | 16 | use App\Kernel\Http\Response; 17 | use Hyperf\Di\Annotation\Inject; 18 | use Hyperf\HttpServer\Contract\RequestInterface; 19 | use Psr\Container\ContainerInterface; 20 | use Psr\EventDispatcher\EventDispatcherInterface; 21 | 22 | abstract class AbstractController 23 | { 24 | /** 25 | * @Inject 26 | * @var ContainerInterface 27 | */ 28 | protected $container; 29 | 30 | /** 31 | * @Inject 32 | * @var RequestInterface 33 | */ 34 | protected $request; 35 | 36 | /** 37 | * @Inject 38 | * @var EventDispatcherInterface 39 | */ 40 | protected $eventDispatcher; 41 | 42 | /** 43 | * @Inject 44 | * @var Response 45 | */ 46 | protected $response; 47 | 48 | /** 49 | * @return int|mixed 50 | */ 51 | public function uid(): int 52 | { 53 | $user = $this->request->getAttribute('user'); 54 | return $user['id'] ?? 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/Controller/DownloadController.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Controller; 15 | 16 | use App\Helper\ValidateHelper; 17 | use App\Model\ChatRecords; 18 | use App\Model\ChatRecordsFile; 19 | use App\Model\UsersGroup; 20 | use Hyperf\Filesystem\FilesystemFactory; 21 | use League\Flysystem\FileExistsException; 22 | use League\Flysystem\FileNotFoundException; 23 | use Psr\Http\Message\ResponseInterface; 24 | 25 | /** 26 | * Class DownloadController 27 | * @package App\Controller 28 | * @TODO 待解决下载文件不完整的问题 29 | */ 30 | class DownloadController extends AbstractController 31 | { 32 | public function userChatFile() : ResponseInterface 33 | { 34 | $crId = (int)$this->request->input('cr_id', 0); 35 | $uid = $this->uid(); 36 | 37 | if (!ValidateHelper::isInteger($crId)) { 38 | return $this->response->error('文件下载失败...'); 39 | } 40 | 41 | /** 42 | * @var ChatRecords $recordsInfo 43 | */ 44 | $recordsInfo = ChatRecords::select(['msg_type', 'source', 'user_id', 'receive_id'])->where('id', $crId)->first(); 45 | if (!$recordsInfo) { 46 | return $this->response->error('文件不存在...'); 47 | } 48 | 49 | //判断消息是否是当前用户发送(如果是则跳过权限验证) 50 | if ($recordsInfo->user_id !== $uid) { 51 | if ($recordsInfo->source === 1) { 52 | if ($recordsInfo->receive_id !== $uid) { 53 | return $this->response->error('非法请求...'); 54 | } 55 | } elseif (!UsersGroup::isMember($recordsInfo->receive_id, $uid)) { 56 | return $this->response->error('非法请求...'); 57 | } 58 | } 59 | 60 | /** 61 | * @var ChatRecordsFile $fileInfo 62 | */ 63 | $fileInfo = ChatRecordsFile::select(['save_dir', 'original_name'])->where('record_id', $crId)->first(); 64 | if (!$fileInfo) { 65 | return $this->response->error('文件不存在或没有下载权限...'); 66 | } 67 | 68 | $factory = di(FilesystemFactory::class)->get('qiniu'); 69 | if ($factory->has($fileInfo->save_dir)) { 70 | $dir = config('file.storage.local.root'); 71 | 72 | $fileSystem = di(FilesystemFactory::class)->get('local'); 73 | 74 | try { 75 | if ($fileSystem->has($fileInfo->save_dir)) { 76 | return $this->response->download($dir . '/' . $fileInfo->save_dir, $fileInfo->original_name); 77 | } 78 | $contents = $factory->read($fileInfo->save_dir); 79 | $fileSystem->write($fileInfo->save_dir, $contents); 80 | return $this->response->download($dir . '/' . $fileInfo->save_dir, $fileInfo->original_name); 81 | } catch (FileNotFoundException | FileExistsException $e) { 82 | return $this->response->error('文件不存在...'); 83 | } 84 | } 85 | return $this->response->error('文件不存在...'); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /app/Controller/HealthController.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Controller; 15 | 16 | use Psr\Http\Message\ResponseInterface; 17 | 18 | class HealthController extends AbstractController 19 | { 20 | public function health(): ResponseInterface 21 | { 22 | return $this->response->success('ok'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Controller/TestOneController.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Controller; 15 | 16 | use App\Component\Mail; 17 | use App\JsonRpc\Contract\InterfaceUserService; 18 | use Hyperf\Di\Annotation\Inject; 19 | use Hyperf\HttpServer\Annotation\Controller; 20 | use Hyperf\HttpServer\Annotation\RequestMapping; 21 | use Hyperf\HttpServer\Contract\RequestInterface; 22 | use Hyperf\HttpServer\Contract\ResponseInterface; 23 | 24 | /** 25 | * Class TestController. 26 | * @Controller(prefix="test") 27 | */ 28 | class TestOneController extends AbstractController 29 | { 30 | /** 31 | * @Inject 32 | * @var \Hyperf\Contract\StdoutLoggerInterface 33 | */ 34 | protected $logger; 35 | 36 | /** 37 | * @RequestMapping(path="index") 38 | * 39 | * @return \Psr\Http\Message\ResponseInterface 40 | */ 41 | public function index(RequestInterface $request, ResponseInterface $response) 42 | { 43 | dump($this->request); 44 | dump('asdasdasd22222222'); 45 | return $response->raw('Hello Hyperf!'); 46 | } 47 | 48 | /** 49 | * @RequestMapping(path="logger") 50 | */ 51 | public function logger() 52 | { 53 | dump($this->logger); 54 | dump($this->request); 55 | } 56 | 57 | /** 58 | * @RequestMapping(path="rpc") 59 | */ 60 | public function rpc() 61 | { 62 | $user = $this->container->get(InterfaceUserService::class); 63 | return $user->get(1); 64 | // dump($ret); 65 | } 66 | 67 | /** 68 | * @RequestMapping(path="mail") 69 | */ 70 | public function mail() 71 | { 72 | $mail = di(Mail::class); 73 | dump($mail->send(Mail::CHANGE_EMAIL, '绑定邮箱', '213213@qq.com')); 74 | } 75 | 76 | /** 77 | * @RequestMapping(path="waiter") 78 | */ 79 | public function waiter(){ 80 | // 读取用户信息 81 | $user = wait(function() { 82 | return Users::query()->find(1); 83 | }); 84 | 85 | // 请求第三方接口 86 | $client->request($user); 87 | 88 | // 保存用户信息 89 | $user->save(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/Controller/UploadController.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Controller; 15 | 16 | use App\Component\SplitUpload; 17 | use Hyperf\Filesystem\FilesystemFactory; 18 | use Psr\Http\Message\ResponseInterface; 19 | 20 | class UploadController extends AbstractController 21 | { 22 | public function fileStream(FilesystemFactory $factory): ResponseInterface 23 | { 24 | $filesystem = $factory->get('qiniu'); 25 | $fileStream = $this->request->post('fileStream', ''); 26 | 27 | $data = base64_decode(str_replace(['data:image/png;base64,', ' '], ['', '+'], $fileStream)); 28 | $path = 'media/images/avatar/' . date('Ymd') . '/' . uniqid('', false) . date('His') . '.png'; 29 | 30 | if (! $filesystem->put($path, $data)) { 31 | return $this->response->error('文件保存失败'); 32 | } 33 | 34 | return $this->response->success('文件上传成功...', ['avatar' => get_media_url($path)]); 35 | } 36 | 37 | /** 38 | * @throws \Exception 39 | */ 40 | public function getFileSplitInfo(): ResponseInterface 41 | { 42 | if (! $this->request->has(['file_name', 'file_size'])) { 43 | return $this->response->parmasError(); 44 | } 45 | 46 | $logic = new SplitUpload($this->uid()); 47 | $data = $logic->createSplitInfo($this->request->input('file_name'), $this->request->input('file_size')); 48 | 49 | return $data ? $this->response->success('success', $data) : $this->response->error('获取文件拆分信息失败...'); 50 | } 51 | 52 | public function fileSubareaUpload(): ResponseInterface 53 | { 54 | $file = $this->request->file('file'); 55 | 56 | $params = ['name', 'hash', 'ext', 'size', 'split_index', 'split_num']; 57 | if (! $this->request->has($params) || ! $file->isValid()) { 58 | return $this->response->parmasError(); 59 | } 60 | 61 | $info = $this->request->inputs($params); 62 | $fileSize = $file->getSize(); 63 | 64 | $logic = new SplitUpload($this->uid()); 65 | 66 | if (! $uploadRes = $logic->saveSplitFile($file, $info['hash'], (int) $info['split_index'], $fileSize)) { 67 | return $this->response->error('上传文件失败...'); 68 | } 69 | 70 | if (((int) $info['split_index'] + 1) === (int) $info['split_num']) { 71 | $fileInfo = $logic->fileMerge($info['hash']); 72 | if (! $fileInfo) { 73 | return $this->response->error('上传文件失败...'); 74 | } 75 | 76 | return $this->response->success('文件上传成功...', [ 77 | 'is_file_merge' => true, 78 | 'hash' => $info['hash'], 79 | ]); 80 | } 81 | 82 | return $this->response->success('文件上传成功...', ['is_file_merge' => false]); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/Event/LoginAfterEvent.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Event; 15 | 16 | class LoginAfterEvent 17 | { 18 | /** 19 | * @var int 20 | */ 21 | public $uid; 22 | 23 | /** 24 | * @var string 25 | */ 26 | public $ip; 27 | 28 | public function __construct(int $uid, string $ip) 29 | { 30 | $this->uid = $uid; 31 | $this->ip = $ip; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Exception/BusinessException.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Exception; 15 | 16 | use App\Constants\ErrorCode; 17 | use Hyperf\Server\Exception\ServerException; 18 | use Throwable; 19 | 20 | class BusinessException extends ServerException 21 | { 22 | public function __construct(int $code = 0, string $message = null, Throwable $previous = null) 23 | { 24 | if (is_null($message)) { 25 | $message = ErrorCode::getMessage($code); 26 | } 27 | 28 | parent::__construct($message, $code, $previous); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Exception/Handler/AppExceptionHandler.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Exception\Handler; 15 | 16 | use Hyperf\Contract\StdoutLoggerInterface; 17 | use Hyperf\ExceptionHandler\ExceptionHandler; 18 | use Hyperf\HttpMessage\Stream\SwooleStream; 19 | use Psr\Http\Message\ResponseInterface; 20 | use Throwable; 21 | 22 | class AppExceptionHandler extends ExceptionHandler 23 | { 24 | /** 25 | * @var StdoutLoggerInterface 26 | */ 27 | protected $logger; 28 | 29 | public function __construct(StdoutLoggerInterface $logger) 30 | { 31 | $this->logger = $logger; 32 | } 33 | 34 | public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface 35 | { 36 | $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile())); 37 | $this->logger->error($throwable->getTraceAsString()); 38 | return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.')); 39 | } 40 | 41 | public function isValid(Throwable $throwable): bool 42 | { 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Exception/Handler/HandshakeExceptionHandler.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Exception\Handler; 15 | 16 | use App\Exception\HandshakeException; 17 | use Hyperf\Contract\StdoutLoggerInterface; 18 | use Hyperf\ExceptionHandler\ExceptionHandler; 19 | use Hyperf\HttpMessage\Stream\SwooleStream; 20 | use Psr\Http\Message\ResponseInterface; 21 | use Throwable; 22 | 23 | class HandshakeExceptionHandler extends ExceptionHandler 24 | { 25 | private $logger; 26 | 27 | public function __construct(StdoutLoggerInterface $logger) 28 | { 29 | $this->logger = $logger; 30 | } 31 | 32 | public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface 33 | { 34 | if ($throwable instanceof HandshakeException) { 35 | $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile())); 36 | $this->logger->error($throwable->getTraceAsString()); 37 | return $response->withHeader('Server', 'Cloud')->withStatus(401)->withBody(new SwooleStream($throwable->getMessage())); 38 | } 39 | } 40 | 41 | public function isValid(Throwable $throwable): bool 42 | { 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Exception/HandshakeException.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Exception; 15 | 16 | class HandshakeException extends \RuntimeException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /app/Helper/NumberHelper.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Helper; 15 | 16 | use App\Constants\Consts; 17 | 18 | class NumberHelper 19 | { 20 | /** 21 | * 格式化文件比特大小. 22 | * 23 | * @param int $size 文件大小(比特) 24 | * @param int $dec 小数位 25 | * @param string $delimiter 数字和单位间的分隔符 26 | */ 27 | public static function formatBytes(int $size, int $dec = 2, string $delimiter = ''): string 28 | { 29 | $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; 30 | for ($i = 0; $size >= 1024 && $i < 5; ++$i) { 31 | $size /= 1024; 32 | } 33 | 34 | return round($size, $dec) . $delimiter . ($units[$i] ?? Consts::UNKNOWN); 35 | } 36 | 37 | /** 38 | * 值是否在某范围内. 39 | * 40 | * @param float|int $val 值 41 | * @param float|int $min 小值 42 | * @param float|int $max 大值 43 | */ 44 | public static function inRange($val, $min, $max): bool 45 | { 46 | $val = floatval($val); 47 | $min = floatval($min); 48 | $max = floatval($max); 49 | return $val >= $min && $val <= $max; 50 | } 51 | 52 | /** 53 | * 对数列求和,忽略非数值. 54 | * 55 | * @param mixed ...$vals 56 | */ 57 | public static function sum(...$vals): float 58 | { 59 | $res = 0; 60 | foreach ($vals as $val) { 61 | if (is_numeric($val)) { 62 | $res += floatval($val); 63 | } 64 | } 65 | 66 | return $res; 67 | } 68 | 69 | /** 70 | * 对数列求平均值,忽略非数值. 71 | * 72 | * @param mixed ...$vals 73 | */ 74 | public static function average(...$vals): float 75 | { 76 | $res = 0; 77 | $count = 0; 78 | $total = 0; 79 | foreach ($vals as $val) { 80 | if (is_numeric($val)) { 81 | $total += floatval($val); 82 | ++$count; 83 | } 84 | } 85 | 86 | if ($count > 0) { 87 | $res = $total / $count; 88 | } 89 | 90 | return $res; 91 | } 92 | 93 | /** 94 | * 获取地理距离/米. 95 | * 参数分别为两点的经度和纬度.lat:-90~90,lng:-180~180. 96 | * 97 | * @param float $lng1 起点经度 98 | * @param float $lat1 起点纬度 99 | * @param float $lng2 终点经度 100 | * @param float $lat2 终点纬度 101 | */ 102 | public static function geoDistance(float $lng1 = 0, float $lat1 = 0, float $lng2 = 0, float $lat2 = 0): float 103 | { 104 | $earthRadius = 6371000.0; 105 | $lat1 = ($lat1 * pi()) / 180; 106 | $lng1 = ($lng1 * pi()) / 180; 107 | $lat2 = ($lat2 * pi()) / 180; 108 | $lng2 = ($lng2 * pi()) / 180; 109 | 110 | $calcLongitude = $lng2 - $lng1; 111 | $calcLatitude = $lat2 - $lat1; 112 | $stepOne = (sin($calcLatitude / 2) ** 2) + cos($lat1) * cos($lat2) * (sin($calcLongitude / 2) ** 2); 113 | $stepTwo = 2 * asin(min(1, sqrt($stepOne))); 114 | return $earthRadius * $stepTwo; 115 | } 116 | 117 | /** 118 | * 数值格式化. 119 | * 120 | * @param float|int $number 要格式化的数字 121 | * @param int $decimals 小数位数 122 | * @param string $decPoint 小数点 123 | * @param string $thousandssep 千分位符号 124 | */ 125 | public static function numberFormat($number, int $decimals = 2, string $decPoint = '.', string $thousandssep = ''): string 126 | { 127 | return number_format($number, $decimals, $decPoint, $thousandssep); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/JsonRpc/Client/ProxyClient.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\JsonRpc\Client; 15 | 16 | use App\JsonRpc\Contract\InterfaceProxyService; 17 | use Hyperf\RpcClient\AbstractServiceClient; 18 | 19 | /** 20 | * @deprecated 21 | * Class ProxyClient 22 | */ 23 | class ProxyClient extends AbstractServiceClient implements InterfaceProxyService 24 | { 25 | /** 26 | * 定义对应服务提供者的服务名称. 27 | * @var string 28 | */ 29 | protected $serviceName = 'ProxyService'; 30 | 31 | /** 32 | * 定义对应服务提供者的服务协议. 33 | * @var string 34 | */ 35 | protected $protocol = 'jsonrpc'; 36 | 37 | public function publish(string $channel, string $message) 38 | { 39 | } 40 | 41 | public function pushMessage(int $uid, string $message) 42 | { 43 | // TODO: Implement pushMessage() method. 44 | } 45 | 46 | public function broadcast(string $message) 47 | { 48 | // TODO: Implement broadcast() method. 49 | } 50 | 51 | public function group(int $groupId, string $message) 52 | { 53 | // TODO: Implement group() method. 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/JsonRpc/Contract/InterfaceGroupService.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\JsonRpc\Contract; 15 | 16 | interface InterfaceGroupService 17 | { 18 | /** 19 | * 创建群组. 20 | * 21 | * @param array $friendIds 22 | * 23 | * @return mixed 24 | */ 25 | public function create(int $uid, array $groupInfo, $friendIds = []); 26 | 27 | /** 28 | * 解散群组. 29 | * 30 | * @return mixed 31 | */ 32 | public function dismiss(int $groupId, int $uid); 33 | 34 | /** 35 | * 邀请加入群组. 36 | * 37 | * @param array $friendIds 38 | * 39 | * @return mixed 40 | */ 41 | public function invite(int $uid, int $groupId, $friendIds = []); 42 | 43 | /** 44 | * 退出群组. 45 | * 46 | * @return mixed 47 | */ 48 | public function quit(int $uid, int $groupId); 49 | 50 | /** 51 | *踢出群组(管理员特殊权限). 52 | * 53 | * @return mixed 54 | */ 55 | public function removeMember(int $groupId, int $uid, array $memberIds); 56 | 57 | /** 58 | * 设置用户群名片. 59 | * 60 | * @return mixed 61 | */ 62 | public function setGroupCard(int $uid, int $groupId, string $visitCard); 63 | 64 | /** 65 | * 获取用户可邀请加入群组的好友列表. 66 | * 67 | * @return mixed 68 | */ 69 | public function getInviteFriends(int $uid, int $groupId); 70 | 71 | /** 72 | * 获取群组成员列表. 73 | * 74 | * @return mixed 75 | */ 76 | public function getGroupMembers(int $groupId, int $uid); 77 | 78 | /** 79 | * 获取群组公告列表. 80 | * 81 | * @return mixed 82 | */ 83 | public function getGroupNotices(int $uid, int $groupId); 84 | 85 | /** 86 | * 创建/编辑群公告. 87 | * 88 | * @return mixed 89 | */ 90 | public function editNotice(int $uid, int $noticeid, int $groupId, string $title, string $content); 91 | 92 | /** 93 | * 删除群公告(软删除). 94 | * 95 | * @return mixed 96 | */ 97 | public function deleteNotice(int $uid, int $groupId, int $noticeId); 98 | 99 | /** 100 | * 获取群信息接口. 101 | * 102 | * @return mixed 103 | */ 104 | public function detail(int $uid, int $groupId); 105 | } 106 | -------------------------------------------------------------------------------- /app/JsonRpc/Contract/InterfaceProxyService.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\JsonRpc\Contract; 15 | 16 | /** 17 | * @deprecated 18 | * Interface InterfaceProxyService 19 | */ 20 | interface InterfaceProxyService 21 | { 22 | /** 23 | *@deprecated 24 | * 25 | * @return mixed 26 | */ 27 | public function pushMessage(int $uid, string $message); 28 | 29 | /** 30 | * @deprecated 31 | * 32 | * @return mixed 33 | */ 34 | public function broadcast(string $message); 35 | 36 | /** 37 | * @deprecated 38 | * 39 | * @return mixed 40 | */ 41 | public function group(int $groupId, string $message); 42 | } 43 | -------------------------------------------------------------------------------- /app/JsonRpc/Contract/InterfaceUserService.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\JsonRpc\Contract; 15 | 16 | use App\Constants\User; 17 | 18 | interface InterfaceUserService 19 | { 20 | public function register(string $mobile, string $password, string $smsCode, string $nickname); 21 | 22 | public function login(string $mobile, string $password); 23 | 24 | public function logout(string $token); 25 | 26 | public function sendVerifyCode(string $mobile, string $type = User::REGISTER); 27 | 28 | public function forgetPassword(string $mobile, string $smsCode, string $password); 29 | 30 | public function get(int $uid): ?array; 31 | 32 | public function checkToken(string $token); 33 | 34 | public function decodeToken(string $token); 35 | } 36 | -------------------------------------------------------------------------------- /app/JsonRpc/ProxyService.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\JsonRpc; 15 | 16 | use App\JsonRpc\Contract\InterfaceProxyService; 17 | use App\Task\CloudTask; 18 | use Hyperf\Logger\LoggerFactory; 19 | use Hyperf\RpcServer\Annotation\RpcService; 20 | use Hyperf\Utils\Coroutine; 21 | use Psr\Container\ContainerInterface; 22 | 23 | /** 24 | * 该类暂时废弃. 25 | * @deprecated 26 | * Class Cloud 27 | * @RpcService(name="ProxyService", protocol="jsonrpc-tcp-length-check", server="jsonrpc", publishTo="consul") 28 | */ 29 | class ProxyService implements InterfaceProxyService 30 | { 31 | /** 32 | * @var ContainerInterface 33 | */ 34 | protected $container; 35 | 36 | /** 37 | * @var \Psr\Log\LoggerInterface 38 | */ 39 | private $logger; 40 | 41 | public function __construct(ContainerInterface $container) 42 | { 43 | $this->container = $container; 44 | $this->logger = $container->get(LoggerFactory::class)->get(); 45 | } 46 | 47 | /** 48 | * @deprecated 49 | * 单点推送 50 | * Message: 51 | *{"event":"event_talk","data":{"send_user":4166,"receive_user":"4167","source_type":"1","text_message":"1"}} 52 | * 53 | * @param int $uid 用户的唯一ID 54 | * 55 | * @return mixed|void 56 | */ 57 | public function pushMessage(int $uid, string $message) 58 | { 59 | $this->logger->debug('cloud node: pushmsg'); 60 | if (empty($keys) || empty($message)) { 61 | $this->logger->error('cloud json-rpc pushmsg keys message is empty raw data'); 62 | return; 63 | } 64 | di(CloudTask::class)->push((string) $uid, $message); 65 | } 66 | 67 | /** 68 | * 广播. 69 | * @deprecated 70 | */ 71 | public function broadcast(string $message) 72 | { 73 | if (empty($message)) { 74 | $this->logger->error('cloud json-rpc broadcast message is empty raw data'); 75 | return; 76 | } 77 | $this->logger->debug(sprintf('cloud json-rpc broadcast message:%s', $message)); 78 | Coroutine::create(function () use ($message) { 79 | di(CloudTask::class)->broadcast($message); 80 | }); 81 | } 82 | 83 | /** 84 | * @deprecated 85 | * 群聊推送 86 | * Message: 87 | * {"event":"event_talk","data":{"send_user":4166,"receive_user":"117","source_type":"2","text_message":"2"}} 88 | * 89 | * @param int $groupId 群聊ID 90 | */ 91 | public function group(int $groupId, string $message) 92 | { 93 | if (empty($message)) { 94 | $this->logger->error('cloud json-rpc broadcast message is empty raw data'); 95 | return; 96 | } 97 | $this->logger->debug(sprintf('cloud json-rpc broadcast message:%s', $message)); 98 | Coroutine::create(function () use ($groupId, $message) { 99 | di(CloudTask::class)->group($groupId, $message); 100 | }); 101 | } 102 | 103 | /** 104 | * @deprecated 105 | */ 106 | public function publish(string $channel, string $message) 107 | { 108 | if (empty($message)) { 109 | $this->logger->error('cloud json-rpc publish message is empty raw data'); 110 | return; 111 | } 112 | if (empty($channel)) { 113 | $this->logger->error('cloud json-rpc publish channel is empty raw data'); 114 | return; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/Kernel/ClassMap/Coroutine.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace Hyperf\Utils; 15 | 16 | use App\Kernel\Context\Coroutine as Go; 17 | use Hyperf\Engine\Coroutine as Co; 18 | use Hyperf\Engine\Exception\CoroutineDestroyedException; 19 | use Hyperf\Engine\Exception\RunningInNonCoroutineException; 20 | 21 | class Coroutine 22 | { 23 | /** 24 | * Returns the current coroutine ID. 25 | * Returns -1 when running in non-coroutine context. 26 | */ 27 | public static function id(): int 28 | { 29 | return Co::id(); 30 | } 31 | 32 | public static function defer(callable $callable): void 33 | { 34 | Co::defer($callable); 35 | } 36 | 37 | public static function sleep(float $seconds): void 38 | { 39 | usleep((int) ($seconds * 1000 * 1000)); 40 | } 41 | 42 | /** 43 | * Returns the parent coroutine ID. 44 | * Returns 0 when running in the top level coroutine. 45 | * 46 | * @param null|int $coroutineId 47 | * 48 | * @return int 49 | */ 50 | public static function parentId(?int $coroutineId = null): int 51 | { 52 | return Co::pid($coroutineId); 53 | } 54 | 55 | /** 56 | * @param callable $callable 57 | * 58 | * @return int Returns the coroutine ID of the coroutine just created. 59 | * Returns -1 when coroutine create failed. 60 | */ 61 | public static function create(callable $callable): int 62 | { 63 | return di()->get(Go::class)->create($callable); 64 | } 65 | 66 | public static function inCoroutine(): bool 67 | { 68 | return Co::id() > 0; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/Kernel/Context/Coroutine.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\Context; 15 | 16 | use App\Kernel\Log\AppendRequestIdProcessor; 17 | use Hyperf\Contract\StdoutLoggerInterface; 18 | use Hyperf\ExceptionHandler\Formatter\FormatterInterface; 19 | use Hyperf\Utils; 20 | use Psr\Container\ContainerInterface; 21 | use Psr\Http\Message\ServerRequestInterface; 22 | use Hyperf\Engine\Coroutine as Co; 23 | use Throwable; 24 | 25 | class Coroutine 26 | { 27 | /** 28 | * @var ContainerInterface 29 | */ 30 | protected $container; 31 | 32 | /** 33 | * @var StdoutLoggerInterface 34 | */ 35 | protected $logger; 36 | 37 | /** 38 | * @var null|FormatterInterface 39 | */ 40 | protected $formatter; 41 | 42 | public function __construct(ContainerInterface $container) 43 | { 44 | $this->container = $container; 45 | $this->logger = $container->get(StdoutLoggerInterface::class); 46 | if ($container->has(FormatterInterface::class)) { 47 | $this->formatter = $container->get(FormatterInterface::class); 48 | } 49 | } 50 | 51 | /** 52 | * @param callable $callable 53 | * 54 | * @return int Returns the coroutine ID of the coroutine just created. 55 | * Returns -1 when coroutine create failed. 56 | */ 57 | public function create(callable $callable): int 58 | { 59 | $id = Utils\Coroutine::id(); 60 | $coroutine = Co::create(function () use ($callable, $id) { 61 | try { 62 | // Shouldn't copy all contexts to avoid socket already been bound to another coroutine. 63 | Utils\Context::copy($id, [ 64 | AppendRequestIdProcessor::TRACE_ID, 65 | ServerRequestInterface::class, 66 | ]); 67 | call($callable); 68 | } catch (Throwable $throwable) { 69 | $this->logger->warning((string) $throwable); 70 | } 71 | }); 72 | 73 | try { 74 | return $coroutine->getId(); 75 | } catch (\Throwable $throwable) { 76 | $this->logger->warning((string) $throwable); 77 | return -1; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/Kernel/Http/Response.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\Http; 15 | 16 | use Hyperf\HttpMessage\Cookie\Cookie; 17 | use Hyperf\HttpMessage\Exception\HttpException; 18 | use Hyperf\HttpMessage\Stream\SwooleStream; 19 | use Hyperf\HttpServer\Contract\ResponseInterface; 20 | use Hyperf\Utils\Context; 21 | use Psr\Container\ContainerInterface; 22 | use Psr\Http\Message\ResponseInterface as PsrResponseInterface; 23 | 24 | class Response 25 | { 26 | /** 27 | * @var ContainerInterface 28 | */ 29 | protected $container; 30 | 31 | /** 32 | * @var ResponseInterface 33 | */ 34 | protected $response; 35 | 36 | public function __construct(ContainerInterface $container) 37 | { 38 | $this->container = $container; 39 | $this->response = $container->get(ResponseInterface::class); 40 | } 41 | 42 | public function success(string $message = '', $data = []): PsrResponseInterface 43 | { 44 | return $this->response->json([ 45 | 'code' => 200, 46 | 'data' => $data, 47 | 'msg' => $message, 48 | ]); 49 | } 50 | 51 | public function fail($code, $message = ''): PsrResponseInterface 52 | { 53 | return $this->response->json([ 54 | 'code' => $code, 55 | 'msg' => $message, 56 | ]); 57 | } 58 | 59 | public function parmasError($message = '请求参数错误'): PsrResponseInterface 60 | { 61 | return $this->response->json([ 62 | 'code' => 301, 63 | 'msg' => $message, 64 | ]); 65 | } 66 | 67 | public function error(string $msg, $data = []): PsrResponseInterface 68 | { 69 | return $this->response->json([ 70 | 'code' => 305, 71 | 'msg' => $msg, 72 | 'data' => $data, 73 | ]); 74 | } 75 | 76 | public function redirect($url, $status = 302): PsrResponseInterface 77 | { 78 | return $this->response() 79 | ->withAddedHeader('Location', (string) $url) 80 | ->withStatus($status); 81 | } 82 | 83 | public function cookie(Cookie $cookie) 84 | { 85 | $response = $this->response()->withCookie($cookie); 86 | Context::set(PsrResponseInterface::class, $response); 87 | return $this; 88 | } 89 | 90 | public function handleException(HttpException $throwable): PsrResponseInterface 91 | { 92 | return $this->response() 93 | ->withAddedHeader('Server', 'Hyperf') 94 | ->withStatus($throwable->getStatusCode()) 95 | ->withBody(new SwooleStream($throwable->getMessage())); 96 | } 97 | 98 | public function response(): PsrResponseInterface 99 | { 100 | return Context::get(PsrResponseInterface::class); 101 | } 102 | 103 | public function toWechatXML(string $xml, int $statusCode = 200): PsrResponseInterface 104 | { 105 | return $this->response() 106 | ->withStatus($statusCode) 107 | ->withAddedHeader('content-type', 'application/xml; charset=utf-8') 108 | ->withBody(new SwooleStream($xml)); 109 | } 110 | 111 | public function download(string $file, string $name = ''): PsrResponseInterface 112 | { 113 | return $this->container->get(ResponseInterface::class)->download($file, $name); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/Kernel/Log/AppendRequestIdProcessor.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\Log; 15 | 16 | use App\Constants\Log; 17 | use App\Helper\StringHelper; 18 | use Hyperf\Utils\Context; 19 | use Hyperf\WebSocketServer\Context as WsContext; 20 | use Monolog\Processor\ProcessorInterface; 21 | 22 | class AppendRequestIdProcessor implements ProcessorInterface 23 | { 24 | public const TRACE_ID = 'log.trace.id'; 25 | 26 | public function __invoke(array $record): array 27 | { 28 | $record['context']['trace_id'] = Context::getOrSet(self::TRACE_ID, StringHelper::randSimple(20)); 29 | $record['context'][Log::CONTEXT_KEY] = WsContext::get(Log::CONTEXT_KEY); 30 | return $record; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Kernel/Log/LoggerFactory.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\Log; 15 | 16 | use Hyperf\Logger\LoggerFactory as HyperfLoggerFactory; 17 | use Psr\Container\ContainerInterface; 18 | 19 | class LoggerFactory 20 | { 21 | public function __invoke(ContainerInterface $container) 22 | { 23 | return $container->get(HyperfLoggerFactory::class)->make(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Kernel/SocketIO/SocketIOFactory.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\SocketIO; 15 | 16 | use Hyperf\Contract\StdoutLoggerInterface; 17 | use Hyperf\SocketIOServer\Parser\Decoder; 18 | use Hyperf\SocketIOServer\Parser\Encoder; 19 | use Hyperf\SocketIOServer\SidProvider\SidProviderInterface; 20 | use Hyperf\SocketIOServer\SocketIO; 21 | use Hyperf\WebSocketServer\Sender; 22 | use Psr\Container\ContainerInterface; 23 | 24 | class SocketIOFactory 25 | { 26 | public function __invoke(ContainerInterface $container): SocketIO 27 | { 28 | $io = new SocketIO( 29 | $container->get(StdoutLoggerInterface::class), 30 | $container->get(Sender::class), 31 | $container->get(Decoder::class), 32 | $container->get(Encoder::class), 33 | $container->get(SidProviderInterface::class) 34 | ); 35 | 36 | // 重写参数,参考https://hyperf.wiki/2.0/#/zh-cn/socketio-server?id=%e4%bf%ae%e6%94%b9-socketio-%e5%9f%ba%e7%a1%80%e5%8f%82%e6%95%b0 37 | $io->setPingTimeout(10000); 38 | $io->setPingInterval(10000); 39 | $io->setClientCallbackTimeout(10000); 40 | 41 | return $io; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/Client.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket; 15 | 16 | use App\Kernel\WebSocket\Exception\InvalidWebSocketConnectionException; 17 | use App\Kernel\WebSocket\Pool\PoolFactory; 18 | use Hyperf\Utils\Context; 19 | 20 | class Client 21 | { 22 | /** 23 | * @var PoolFactory 24 | */ 25 | protected $factory; 26 | 27 | /** 28 | * @var string 29 | */ 30 | protected $poolName = 'default'; 31 | 32 | public function __construct(PoolFactory $factory) 33 | { 34 | $this->factory = $factory; 35 | } 36 | 37 | public function __call($name, $arguments) 38 | { 39 | // Get a connection from coroutine context or connection pool. 40 | $hasContextConnection = Context::has($this->getContextKey()); 41 | $connection = $this->getConnection($hasContextConnection); 42 | 43 | try { 44 | $connection = $connection->getConnection(); 45 | // Execute the command with the arguments. 46 | $result = $connection->{$name}(...$arguments); 47 | } finally { 48 | // Release connection. 49 | if (! $hasContextConnection) { 50 | $connection->release(); 51 | } 52 | } 53 | 54 | return $result; 55 | } 56 | 57 | /** 58 | * Get a connection from coroutine context, or from redis connectio pool. 59 | * 60 | * @param mixed $hasContextConnection 61 | */ 62 | private function getConnection($hasContextConnection): ClientConnection 63 | { 64 | $connection = null; 65 | if ($hasContextConnection) { 66 | $connection = Context::get($this->getContextKey()); 67 | } 68 | if (! $connection instanceof ClientConnection) { 69 | $pool = $this->factory->getPool($this->poolName); 70 | $connection = $pool->get(); 71 | } 72 | if (! $connection instanceof ClientConnection) { 73 | throw new InvalidWebSocketConnectionException('The connection is not a valid WebSocketClientConnection.'); 74 | } 75 | return $connection; 76 | } 77 | 78 | /** 79 | * The key to identify the connection object in coroutine context. 80 | */ 81 | private function getContextKey(): string 82 | { 83 | return sprintf('websocket.connection.%s', $this->poolName); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/ClientConnection.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket; 15 | 16 | use Hyperf\Contract\ConnectionInterface; 17 | use Hyperf\Contract\StdoutLoggerInterface; 18 | use Hyperf\HttpMessage\Uri\Uri; 19 | use Hyperf\Pool\Connection as BaseConnection; 20 | use Hyperf\Pool\Exception\ConnectionException; 21 | use Hyperf\Pool\Pool; 22 | use Hyperf\WebSocketClient\Client as BClient; 23 | use Psr\Container\ContainerInterface; 24 | 25 | class ClientConnection extends BaseConnection implements ConnectionInterface 26 | { 27 | /** 28 | * @var BClient 29 | */ 30 | protected $connection; 31 | 32 | protected $config = [ 33 | 'host' => '127.0.0.1', 34 | 'port' => '9502', 35 | 'ws' => 'ws://', 36 | 'auto_close' => false, 37 | ]; 38 | 39 | public function __construct(ContainerInterface $container, Pool $pool, array $config) 40 | { 41 | parent::__construct($container, $pool); 42 | $this->config = array_replace($this->config, $config); 43 | 44 | $this->reconnect(); 45 | } 46 | 47 | public function __call($name, $arguments) 48 | { 49 | try { 50 | $result = $this->connection->{$name}(...$arguments); 51 | } catch (\Throwable $exception) { 52 | $result = $this->retry($name, $arguments, $exception); 53 | } 54 | 55 | return $result; 56 | } 57 | 58 | public function getActiveConnection() 59 | { 60 | if ($this->check()) { 61 | return $this; 62 | } 63 | 64 | if (! $this->reconnect()) { 65 | throw new ConnectionException('Connection reconnect failed.'); 66 | } 67 | 68 | return $this; 69 | } 70 | 71 | public function release(): void 72 | { 73 | parent::release(); 74 | } 75 | 76 | public function reconnect(): bool 77 | { 78 | $host = $this->config['host']; 79 | $port = $this->config['port']; 80 | $ws = $this->config['ws']; 81 | $autoClose = $this->config['auto_close']; 82 | $uri = sprintf('%s%s:%s', $ws, $host, $port); 83 | $client = make(BClient::class, ['uri' => new Uri($uri)]); 84 | if (! $client instanceof BClient) { 85 | throw new ConnectionException('Connection reconnect failed.'); 86 | } 87 | $this->connection = $client; 88 | $this->lastUseTime = microtime(true); 89 | 90 | if ($autoClose) { 91 | defer(function () use ($client) { 92 | $client->close(); 93 | }); 94 | } 95 | 96 | return true; 97 | } 98 | 99 | public function close(): bool 100 | { 101 | $this->connection->close(); 102 | unset($this->connection); 103 | 104 | return true; 105 | } 106 | 107 | protected function retry($name, $arguments, \Throwable $exception) 108 | { 109 | $logger = $this->container->get(StdoutLoggerInterface::class); 110 | $logger->warning(sprintf('WebSocket::__call failed, because ' . $exception->getMessage())); 111 | 112 | try { 113 | $this->reconnect(); 114 | $result = $this->connection->{$name}(...$arguments); 115 | } catch (\Throwable $exception) { 116 | $this->lastUseTime = 0.0; 117 | throw $exception; 118 | } 119 | 120 | return $result; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/ClientFactory.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket; 15 | 16 | use App\Kernel\WebSocket\Exception\InvalidWebSocketProxyException; 17 | use Hyperf\Contract\ConfigInterface; 18 | 19 | class ClientFactory 20 | { 21 | /** 22 | * @var ClientProxy[] 23 | */ 24 | protected $proxies; 25 | 26 | public function __construct(ConfigInterface $config) 27 | { 28 | $clientConfig = $config->get('websocket_client'); 29 | 30 | foreach ($clientConfig as $poolName => $item) { 31 | $this->proxies[$poolName] = make(ClientProxy::class, ['pool' => $poolName]); 32 | } 33 | } 34 | 35 | /** 36 | * @return ClientProxy|\Hyperf\WebSocketClient\Client 37 | */ 38 | public function get(string $poolName) 39 | { 40 | $proxy = $this->proxies[$poolName] ?? null; 41 | if (! $proxy instanceof ClientProxy) { 42 | throw new InvalidWebSocketProxyException('Invalid WebSocketClient proxy.'); 43 | } 44 | 45 | return $proxy; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/ClientProxy.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket; 15 | 16 | use App\Kernel\WebSocket\Pool\PoolFactory; 17 | 18 | class ClientProxy extends Client 19 | { 20 | protected $poolName; 21 | 22 | public function __construct(PoolFactory $factory, string $pool) 23 | { 24 | parent::__construct($factory); 25 | 26 | $this->poolName = $pool; 27 | } 28 | 29 | public function __call($name, $arguments) 30 | { 31 | return parent::__call($name, $arguments); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/Exception/InvalidWebSocketConnectionException.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket\Exception; 15 | 16 | use RuntimeException; 17 | 18 | class InvalidWebSocketConnectionException extends RuntimeException 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/Exception/InvalidWebSocketProxyException.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket\Exception; 15 | 16 | class InvalidWebSocketProxyException extends \RuntimeException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/Pool/ClientPool.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket\Pool; 15 | 16 | use App\Kernel\WebSocket\ClientConnection; 17 | use Hyperf\Contract\ConfigInterface; 18 | use Hyperf\Contract\ConnectionInterface; 19 | use Hyperf\Pool\Frequency; 20 | use Hyperf\Pool\Pool as BPool; 21 | use Hyperf\Utils\Arr; 22 | use Psr\Container\ContainerInterface; 23 | 24 | class ClientPool extends BPool 25 | { 26 | /** 27 | * @var string 28 | */ 29 | protected $name; 30 | 31 | /** 32 | * @var array 33 | */ 34 | protected $config; 35 | 36 | /** 37 | * Pool constructor. 38 | */ 39 | public function __construct(ContainerInterface $container, string $name) 40 | { 41 | $this->name = $name; 42 | $config = $container->get(ConfigInterface::class); 43 | $key = sprintf('websocket_client.%s', $this->name); 44 | if (! $config->has($key)) { 45 | throw new \InvalidArgumentException(sprintf('config[%s] is not exist!', $key)); 46 | } 47 | 48 | $this->config = $config->get($key); 49 | $options = Arr::get($this->config, 'pool', []); 50 | 51 | $this->frequency = make(Frequency::class); 52 | 53 | parent::__construct($container, $options); 54 | } 55 | 56 | protected function createConnection(): ConnectionInterface 57 | { 58 | return new ClientConnection($this->container, $this, $this->config); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Kernel/WebSocket/Pool/PoolFactory.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Kernel\WebSocket\Pool; 15 | 16 | use Hyperf\Di\Container; 17 | use Psr\Container\ContainerInterface; 18 | 19 | class PoolFactory 20 | { 21 | /** 22 | * @var ContainerInterface 23 | */ 24 | protected $container; 25 | 26 | /** 27 | * @var ClientPool[] 28 | */ 29 | protected $pools = []; 30 | 31 | public function __construct(ContainerInterface $container) 32 | { 33 | $this->container = $container; 34 | } 35 | 36 | public function getPool(string $name): ClientPool 37 | { 38 | if (isset($this->pools[$name])) { 39 | return $this->pools[$name]; 40 | } 41 | 42 | if ($this->container instanceof Container) { 43 | $pool = $this->container->make(ClientPool::class, ['name' => $name]); 44 | } else { 45 | $pool = new ClientPool($this->container, $name); 46 | } 47 | return $this->pools[$name] = $pool; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/Listener/DbQueryExecutedListener.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Listener; 15 | 16 | use Hyperf\Database\Events\QueryExecuted; 17 | use Hyperf\Event\Annotation\Listener; 18 | use Hyperf\Event\Contract\ListenerInterface; 19 | use Hyperf\Logger\LoggerFactory; 20 | use Hyperf\Utils\Arr; 21 | use Hyperf\Utils\Str; 22 | use Psr\Container\ContainerInterface; 23 | use Psr\Log\LoggerInterface; 24 | 25 | /** 26 | * @Listener 27 | */ 28 | class DbQueryExecutedListener implements ListenerInterface 29 | { 30 | /** 31 | * @var LoggerInterface 32 | */ 33 | private $logger; 34 | 35 | public function __construct(ContainerInterface $container) 36 | { 37 | $this->logger = $container->get(LoggerFactory::class)->get('sql', 'sql'); 38 | } 39 | 40 | public function listen(): array 41 | { 42 | return [ 43 | QueryExecuted::class, 44 | ]; 45 | } 46 | 47 | public function process(object $event) 48 | { 49 | if ($event instanceof QueryExecuted) { 50 | $sql = $event->sql; 51 | if (! Arr::isAssoc($event->bindings)) { 52 | foreach ($event->bindings as $key => $value) { 53 | $sql = Str::replaceFirst('?', "'{$value}'", $sql); 54 | } 55 | } 56 | 57 | $this->logger->info(sprintf('[%s] %s', $event->time, $sql)); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Listener/LoginAfterListener.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Listener; 15 | 16 | use App\Event\LoginAfterEvent; 17 | use App\Model\UserLoginLog; 18 | use Carbon\Carbon; 19 | use Hyperf\Event\Contract\ListenerInterface; 20 | 21 | class LoginAfterListener implements ListenerInterface 22 | { 23 | public function listen(): array 24 | { 25 | return [ 26 | LoginAfterEvent::class, 27 | ]; 28 | } 29 | 30 | public function process(object $event): void 31 | { 32 | if ($event instanceof LoginAfterEvent) { 33 | UserLoginLog::create([ 34 | 'user_id' => $event->uid, 35 | 'ip' => $event->ip, 36 | 'created_at' => Carbon::now()->toDateTimeString(), 37 | ]); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Listener/OnShutdownListener.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Listener; 15 | 16 | use App\Component\Command\Server; 17 | use Carbon\Carbon; 18 | use Codedungeon\PHPCliColors\Color; 19 | use Hyperf\Contract\ApplicationInterface; 20 | use Hyperf\Event\Contract\ListenerInterface; 21 | use Hyperf\Framework\Event\OnShutdown; 22 | use Hyperf\SocketIOServer\SocketIO; 23 | use Hyperf\Utils\ApplicationContext; 24 | use Symfony\Component\Console\Input\ArrayInput; 25 | use Symfony\Component\Console\Output\NullOutput; 26 | 27 | class OnShutdownListener implements ListenerInterface 28 | { 29 | protected $redisPrefix = 'ws'; 30 | 31 | public function listen(): array 32 | { 33 | return [ 34 | OnShutdown::class, 35 | ]; 36 | } 37 | 38 | public function process(object $event): void 39 | { 40 | if ($event instanceof OnShutdown) { 41 | echo Color::GREEN, sprintf('[%s]', Carbon::now()->toDateTimeString()), ' ', Color::CYAN, 42 | PHP_EOL, 43 | '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', 44 | PHP_EOL, 45 | Server::LOGO, 46 | PHP_EOL, 47 | '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<',PHP_EOL; 48 | echo ' 49 | ______ _______ ______ _______ 50 | ( ___ \ |\ /|( ____ \ ( ___ \ |\ /|( ____ \ 51 | | ( ) )( \ / )| ( \/ | ( ) )( \ / )| ( \/ 52 | | (__/ / \ (_) / | (__ | (__/ / \ (_) / | (__ 53 | | __ ( \ / | __) | __ ( \ / | __) 54 | | ( \ \ ) ( | ( | ( \ \ ) ( | ( 55 | | )___) ) | | | (____/\ | )___) ) | | | (____/\ 56 | |/ \___/ \_/ (_______/ |/ \___/ \_/ (_______/ 57 | 58 | ',PHP_EOL; 59 | $this->socketIoClearCommand(); 60 | echo Color::GREEN,'Clean Up Success!'; 61 | } 62 | } 63 | 64 | /** 65 | * 清除全部socket-io. 66 | * @throws \Exception 67 | */ 68 | private function socketIoClearCommand(): void 69 | { 70 | $command = 'socketio-self:clear'; 71 | 72 | $params = ['command' => $command, 'namespace' => '/', 'serverId' => SocketIO::$serverId]; 73 | 74 | // 可以根据自己的需求, 选择使用的 input/output 75 | $input = new ArrayInput($params); 76 | $output = new NullOutput(); 77 | 78 | $container = ApplicationContext::getContainer(); 79 | 80 | /** @var \Symfony\Component\Console\Application $application */ 81 | $application = $container->get(ApplicationInterface::class); 82 | $application->setAutoExit(false); 83 | 84 | try { 85 | $exitCode = $application->find($command)->run($input, $output); 86 | } catch (\Throwable $throwable) { 87 | echo Color::CYAN,$throwable->getMessage(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/Listener/OnStartListener.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Listener; 15 | 16 | use App\Component\Command\CliTable; 17 | use App\Component\Command\CliTableManipulator; 18 | use App\Component\Command\Server; 19 | use Carbon\Carbon; 20 | use Codedungeon\PHPCliColors\Color; 21 | use Hyperf\Event\Contract\ListenerInterface; 22 | use Hyperf\Framework\Event\BeforeMainServerStart; 23 | 24 | class OnStartListener implements ListenerInterface 25 | { 26 | public function listen(): array 27 | { 28 | return [ 29 | BeforeMainServerStart::class, 30 | ]; 31 | } 32 | 33 | public function process(object $event): void 34 | { 35 | echo Color::GREEN, sprintf('[%s]', Carbon::now()->toDateTimeString()), ' ', Color::CYAN, 36 | PHP_EOL, 37 | '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', 38 | PHP_EOL, 39 | Server::LOGO, 40 | PHP_EOL, 41 | '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', 42 | PHP_EOL, 43 | Color::YELLOW, 44 | ' 45 | _______ _ _______ _______ _______ _______ 46 | |\ /|( ____ \( \ ( ____ \( ___ )( )( ____ \ 47 | | ) ( || ( \/| ( | ( \/| ( ) || () () || ( \/ 48 | | | _ | || (__ | | | | | | | || || || || (__ 49 | | |( )| || __) | | | | | | | || |(_)| || __) 50 | | || || || ( | | | | | | | || | | || ( 51 | | () () || (____/\| (____/\| (____/\| (___) || ) ( || (____/\ 52 | (_______)(_______/(_______/(_______/(_______)|/ \|(_______/ 53 | ',PHP_EOL, 54 | Color::RESET, PHP_EOL, 55 | '----------------------------------------------------------------------', PHP_EOL; 56 | echo Color::YELLOW, '| 基于Hyperf2.1微服务协程框架开发的Socket-IO分布式IM系统 |', PHP_EOL, 57 | '----------------------------------------------------------------------', PHP_EOL; 58 | $data = [ 59 | [ 60 | 'php-version' => PHP_VERSION, 61 | 'swoole-version' => SWOOLE_VERSION, 62 | 'app-name' => env('APP_NAME'), 63 | 'date-time' => Carbon::now()->timestamp, 64 | ], 65 | ]; 66 | $table = new CliTable(); 67 | $table->setTableColor('blue'); 68 | $table->setHeaderColor('cyan'); 69 | $table->addField('PHP-VERSION', 'php-version', false, 'white'); 70 | $table->addField('SWOOLE-VERSION', 'swoole-version', false, 'white'); 71 | $table->addField('APP-NAME', 'app-name', false, 'read'); 72 | $table->addField('Date-Time', 'date-time', new CliTableManipulator('nicetime'), 'red'); 73 | $table->injectData($data); 74 | $table->display(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/Listener/OnWorkerStopListener.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Listener; 15 | 16 | use Carbon\Carbon; 17 | use Codedungeon\PHPCliColors\Color; 18 | use Hyperf\Event\Contract\ListenerInterface; 19 | use Hyperf\Framework\Event\OnWorkerStop; 20 | 21 | class OnWorkerStopListener implements ListenerInterface 22 | { 23 | public function listen(): array 24 | { 25 | return [ 26 | OnWorkerStop::class, 27 | ]; 28 | } 29 | 30 | public function process(object $event): void 31 | { 32 | if ($event instanceof OnWorkerStop) { 33 | if ($event->server->taskworker) { 34 | echo Color::GREEN, sprintf('[%s]', Carbon::now()->toDateTimeString()), ' ', Color::CYAN, 35 | "TaskWorker#{$event->workerId} stoped.",PHP_EOL; 36 | } else { 37 | echo Color::GREEN, sprintf('[%s]', Carbon::now()->toDateTimeString()), ' ', Color::CYAN, 38 | "Worker#{$event->workerId} stoped.",PHP_EOL; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Listener/QueueHandleListener.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Listener; 15 | 16 | use Hyperf\AsyncQueue\Event\AfterHandle; 17 | use Hyperf\AsyncQueue\Event\BeforeHandle; 18 | use Hyperf\AsyncQueue\Event\Event; 19 | use Hyperf\AsyncQueue\Event\FailedHandle; 20 | use Hyperf\AsyncQueue\Event\RetryHandle; 21 | use Hyperf\Event\Annotation\Listener; 22 | use Hyperf\Event\Contract\ListenerInterface; 23 | use Hyperf\ExceptionHandler\Formatter\FormatterInterface; 24 | use Hyperf\Logger\LoggerFactory; 25 | 26 | /** 27 | * @Listener 28 | */ 29 | class QueueHandleListener implements ListenerInterface 30 | { 31 | /** 32 | * @var \Psr\Log\LoggerInterface 33 | */ 34 | protected $logger; 35 | 36 | /** 37 | * @var FormatterInterface 38 | */ 39 | protected $formatter; 40 | 41 | public function __construct(LoggerFactory $loggerFactory, FormatterInterface $formatter) 42 | { 43 | $this->logger = $loggerFactory->get('queue'); 44 | $this->formatter = $formatter; 45 | } 46 | 47 | public function listen(): array 48 | { 49 | return [ 50 | AfterHandle::class, 51 | BeforeHandle::class, 52 | FailedHandle::class, 53 | RetryHandle::class, 54 | ]; 55 | } 56 | 57 | public function process(object $event) 58 | { 59 | if ($event instanceof Event && $event->message->job()) { 60 | $job = $event->message->job(); 61 | $jobClass = get_class($job); 62 | $date = date('Y-m-d H:i:s'); 63 | 64 | switch (true) { 65 | case $event instanceof BeforeHandle: 66 | $this->logger->info(sprintf('[%s] Processing %s.', $date, $jobClass)); 67 | break; 68 | case $event instanceof AfterHandle: 69 | $this->logger->info(sprintf('[%s] Processed %s.', $date, $jobClass)); 70 | break; 71 | case $event instanceof FailedHandle: 72 | $this->logger->error(sprintf('[%s] Failed %s.', $date, $jobClass)); 73 | $this->logger->error($this->formatter->format($event->getThrowable())); 74 | break; 75 | case $event instanceof RetryHandle: 76 | $this->logger->warning(sprintf('[%s] Retried %s.', $date, $jobClass)); 77 | break; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/Middleware/CorsMiddleware.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Middleware; 15 | 16 | use Hyperf\Utils\Context; 17 | use Psr\Http\Message\ResponseInterface; 18 | use Psr\Http\Message\ServerRequestInterface; 19 | use Psr\Http\Server\MiddlewareInterface; 20 | use Psr\Http\Server\RequestHandlerInterface; 21 | 22 | class CorsMiddleware implements MiddlewareInterface 23 | { 24 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 25 | { 26 | $response = Context::get(ResponseInterface::class); 27 | $response = $response->withHeader('Access-Control-Allow-Origin', '*') 28 | ->withHeader('Access-Control-Allow-Credentials', 'true') 29 | // Headers 可以根据实际情况进行改写。 30 | ->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization'); 31 | 32 | Context::set(ResponseInterface::class, $response); 33 | 34 | if ($request->getMethod() === 'OPTIONS') { 35 | return $response; 36 | } 37 | 38 | return $handler->handle($request); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Middleware/HttpAuthMiddleware.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Middleware; 15 | 16 | use App\Component\MessageParser; 17 | use App\JsonRpc\Contract\InterfaceUserService; 18 | use App\Kernel\Http\Response; 19 | use Hyperf\Contract\StdoutLoggerInterface; 20 | use Hyperf\HttpMessage\Stream\SwooleStream; 21 | use Hyperf\Utils\Context; 22 | use Phper666\JWTAuth\Exception\JWTException; 23 | use Phper666\JWTAuth\Exception\TokenValidException; 24 | use Psr\Http\Message\ResponseInterface; 25 | use Psr\Http\Message\ServerRequestInterface; 26 | use Psr\Http\Server\MiddlewareInterface; 27 | use Psr\Http\Server\RequestHandlerInterface; 28 | 29 | class HttpAuthMiddleware implements MiddlewareInterface 30 | { 31 | protected $prefix = 'Bearer'; 32 | 33 | private $response; 34 | 35 | public function __construct(StdoutLoggerInterface $logger, Response $response) 36 | { 37 | $this->response = $response; 38 | } 39 | 40 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 41 | { 42 | $isValidToken = false; 43 | try { 44 | $token = $request->getHeader('Authorization')[0] ?? ''; 45 | if (empty($token)) { 46 | $token = $this->prefix . ' ' . ($request->getQueryParams()['token'] ?? ''); 47 | } 48 | $token = ucfirst($token); 49 | $arr = explode($this->prefix . ' ', $token); 50 | $token = $arr[1] ?? ''; 51 | 52 | if (($token !== '') && di(InterfaceUserService::class)->checkToken($token)) { 53 | $request = $this->setRequestContext($token); 54 | return $handler->handle($request); 55 | } 56 | if (! $isValidToken) { 57 | throw new TokenValidException('Token authentication does not pass', 401); 58 | } 59 | } catch (TokenValidException | JWTException $throwable) { 60 | return $this->response->response()->withHeader('Server', 'Hyperf')->withStatus(401)->withBody(new SwooleStream('Token authentication does not pass')); 61 | } catch (\Throwable $exception) { 62 | if (env('APP_ENV') === 'dev') { 63 | return $this->response->response()->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream(MessageParser::encode([ 64 | 'msg' => $exception->getMessage(), 65 | 'trace' => $exception->getTrace(), 66 | 'line' => $exception->getLine(), 67 | 'file' => $exception->getFile(), 68 | ]))); 69 | } 70 | return $this->response->response()->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('服务端错误!')); 71 | } 72 | } 73 | 74 | private function setRequestContext(string $token): ServerRequestInterface 75 | { 76 | $userData = di(InterfaceUserService::class)->decodeToken($token); 77 | $uid = $userData['cloud_uid'] ?? 0; 78 | $rpcUser = di(InterfaceUserService::class); 79 | $user = $rpcUser->get($uid); 80 | $request = Context::get(ServerRequestInterface::class); 81 | $request = $request->withAttribute('user', $user); 82 | Context::set(ServerRequestInterface::class, $request); 83 | return $request; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/Middleware/SocketIOAuthMiddleware.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Middleware; 15 | 16 | use App\JsonRpc\Contract\InterfaceUserService; 17 | use Hyperf\Contract\StdoutLoggerInterface; 18 | use Phper666\JWTAuth\Exception\TokenValidException; 19 | use Psr\Http\Message\ResponseInterface; 20 | use Psr\Http\Message\ServerRequestInterface; 21 | use Psr\Http\Server\MiddlewareInterface; 22 | use Psr\Http\Server\RequestHandlerInterface; 23 | 24 | class SocketIOAuthMiddleware implements MiddlewareInterface 25 | { 26 | protected $prefix = 'Bearer'; 27 | 28 | private $stdoutLogger; 29 | 30 | private $response; 31 | 32 | public function __construct(StdoutLoggerInterface $logger, \Hyperf\HttpServer\Contract\ResponseInterface $response) 33 | { 34 | $this->stdoutLogger = $logger; 35 | $this->response = $response; 36 | } 37 | 38 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 39 | { 40 | //通过 isAuth 方法拦截握手请求并实现权限检查 41 | if (! $this->isAuth($request)) { 42 | return $this->response->raw('Forbidden'); 43 | } 44 | return $handler->handle($request); 45 | } 46 | 47 | protected function isAuth(ServerRequestInterface $request): bool 48 | { 49 | try { 50 | $isValidToken = false; 51 | $token = $request->getQueryParams()['token'] ?? ''; 52 | if (($token !== '') && di(InterfaceUserService::class)->checkToken($token)) { 53 | return true; 54 | } 55 | if (! $isValidToken) { 56 | throw new TokenValidException('Token authentication does not pass', 401); 57 | } 58 | } catch (\Throwable $throwable) { 59 | $this->stdoutLogger->error(sprintf('[%s] [%s]', $throwable->getMessage(), $throwable->getCode())); 60 | return false; 61 | } 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/Model/Article.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property int $class_id 20 | * @property string $tags_id 21 | * @property string $title 22 | * @property string $abstract 23 | * @property string $image 24 | * @property int $is_asterisk 25 | * @property int $status 26 | * @property \Carbon\Carbon $created_at 27 | * @property \Carbon\Carbon $updated_at 28 | * @property string $deleted_at 29 | */ 30 | class Article extends Model 31 | { 32 | /** 33 | * The table associated with the model. 34 | * 35 | * @var string 36 | */ 37 | protected $table = 'article'; 38 | 39 | /** 40 | * The attributes that are mass assignable. 41 | * 42 | * @var array 43 | */ 44 | protected $fillable = ['id', 'user_id', 'class_id', 'tags_id', 'title', 'abstract', 'image', 'is_asterisk', 'status', 'created_at', 'updated_at', 'deleted_at']; 45 | 46 | /** 47 | * The attributes that should be cast to native types. 48 | * 49 | * @var array 50 | */ 51 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'class_id' => 'integer', 'is_asterisk' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 52 | } 53 | -------------------------------------------------------------------------------- /app/Model/ArticleAnnex.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property int $article_id 20 | * @property string $file_suffix 21 | * @property int $file_size 22 | * @property string $save_dir 23 | * @property string $original_name 24 | * @property int $status 25 | * @property \Carbon\Carbon $created_at 26 | * @property string $deleted_at 27 | */ 28 | class ArticleAnnex extends Model 29 | { 30 | /** 31 | * The table associated with the model. 32 | * 33 | * @var string 34 | */ 35 | protected $table = 'article_annex'; 36 | 37 | /** 38 | * The attributes that are mass assignable. 39 | * 40 | * @var array 41 | */ 42 | protected $fillable = ['id', 'user_id', 'article_id', 'file_suffix', 'file_size', 'save_dir', 'original_name', 'status', 'created_at', 'deleted_at']; 43 | 44 | /** 45 | * The attributes that should be cast to native types. 46 | * 47 | * @var array 48 | */ 49 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'article_id' => 'integer', 'file_size' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; 50 | } 51 | -------------------------------------------------------------------------------- /app/Model/ArticleClass.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property string $class_name 20 | * @property int $sort 21 | * @property int $is_default 22 | * @property \Carbon\Carbon $created_at 23 | */ 24 | class ArticleClass extends Model 25 | { 26 | /** 27 | * The table associated with the model. 28 | * 29 | * @var string 30 | */ 31 | protected $table = 'article_class'; 32 | 33 | /** 34 | * The attributes that are mass assignable. 35 | * 36 | * @var array 37 | */ 38 | protected $fillable = ['id', 'user_id', 'class_name', 'sort', 'is_default', 'created_at']; 39 | 40 | /** 41 | * The attributes that should be cast to native types. 42 | * 43 | * @var array 44 | */ 45 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'sort' => 'integer', 'is_default' => 'integer', 'created_at' => 'datetime']; 46 | } 47 | -------------------------------------------------------------------------------- /app/Model/ArticleDetail.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $article_id 19 | * @property string $md_content 20 | * @property string $content 21 | */ 22 | class ArticleDetail extends Model 23 | { 24 | /** 25 | * The table associated with the model. 26 | * 27 | * @var string 28 | */ 29 | protected $table = 'article_detail'; 30 | 31 | /** 32 | * The attributes that are mass assignable. 33 | * 34 | * @var array 35 | */ 36 | protected $fillable = ['id', 'article_id', 'md_content', 'content']; 37 | 38 | /** 39 | * The attributes that should be cast to native types. 40 | * 41 | * @var array 42 | */ 43 | protected $casts = ['id' => 'integer', 'article_id' => 'integer']; 44 | } 45 | -------------------------------------------------------------------------------- /app/Model/ArticleTag.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property string $tag_name 20 | * @property int $sort 21 | * @property \Carbon\Carbon $created_at 22 | */ 23 | class ArticleTag extends Model 24 | { 25 | /** 26 | * The table associated with the model. 27 | * 28 | * @var string 29 | */ 30 | protected $table = 'article_tags'; 31 | 32 | /** 33 | * The attributes that are mass assignable. 34 | * 35 | * @var array 36 | */ 37 | protected $fillable = ['id', 'user_id', 'tag_name', 'sort', 'created_at']; 38 | 39 | /** 40 | * The attributes that should be cast to native types. 41 | * 42 | * @var array 43 | */ 44 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'sort' => 'integer', 'created_at' => 'datetime']; 45 | } 46 | -------------------------------------------------------------------------------- /app/Model/ChatRecords.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $source 19 | * @property int $msg_type 20 | * @property int $user_id 21 | * @property int $receive_id 22 | * @property string $content 23 | * @property int $is_revoke 24 | * @property \Carbon\Carbon $created_at 25 | */ 26 | class ChatRecords extends Model 27 | { 28 | public $timestamps = false; 29 | 30 | /** 31 | * The table associated with the model. 32 | * 33 | * @var string 34 | */ 35 | protected $table = 'chat_records'; 36 | 37 | /** 38 | * The attributes that are mass assignable. 39 | * 40 | * @var array 41 | */ 42 | protected $fillable = ['id', 'source', 'msg_type', 'user_id', 'receive_id', 'content', 'is_revoke', 'created_at']; 43 | 44 | /** 45 | * The attributes that should be cast to native types. 46 | * 47 | * @var array 48 | */ 49 | protected $casts = ['id' => 'integer', 'source' => 'integer', 'msg_type' => 'integer', 'user_id' => 'integer', 'receive_id' => 'integer', 'is_revoke' => 'integer', 'created_at' => 'datetime']; 50 | } 51 | -------------------------------------------------------------------------------- /app/Model/ChatRecordsCode.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $record_id 19 | * @property int $user_id 20 | * @property string $code_lang 21 | * @property string $code 22 | * @property \Carbon\Carbon $created_at 23 | */ 24 | class ChatRecordsCode extends Model 25 | { 26 | public $timestamps = false; 27 | 28 | /** 29 | * The table associated with the model. 30 | * 31 | * @var string 32 | */ 33 | protected $table = 'chat_records_code'; 34 | 35 | /** 36 | * The attributes that are mass assignable. 37 | * 38 | * @var array 39 | */ 40 | protected $fillable = ['id', 'record_id', 'user_id', 'code_lang', 'code', 'created_at']; 41 | 42 | /** 43 | * The attributes that should be cast to native types. 44 | * 45 | * @var array 46 | */ 47 | protected $casts = ['id' => 'integer', 'record_id' => 'integer', 'user_id' => 'integer', 'created_at' => 'datetime']; 48 | } 49 | -------------------------------------------------------------------------------- /app/Model/ChatRecordsDelete.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $record_id 19 | * @property int $user_id 20 | * @property \Carbon\Carbon $created_at 21 | */ 22 | class ChatRecordsDelete extends Model 23 | { 24 | /** 25 | * The table associated with the model. 26 | * 27 | * @var string 28 | */ 29 | protected $table = 'chat_records_delete'; 30 | 31 | /** 32 | * The attributes that are mass assignable. 33 | * 34 | * @var array 35 | */ 36 | protected $fillable = ['id', 'record_id', 'user_id', 'created_at']; 37 | 38 | /** 39 | * The attributes that should be cast to native types. 40 | * 41 | * @var array 42 | */ 43 | protected $casts = ['id' => 'integer', 'record_id' => 'integer', 'user_id' => 'integer', 'created_at' => 'datetime']; 44 | } 45 | -------------------------------------------------------------------------------- /app/Model/ChatRecordsFile.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $record_id 19 | * @property int $user_id 20 | * @property int $file_source 21 | * @property int $file_type 22 | * @property int $save_type 23 | * @property string $original_name 24 | * @property string $file_suffix 25 | * @property int $file_size 26 | * @property string $save_dir 27 | * @property int $is_delete 28 | * @property \Carbon\Carbon $created_at 29 | */ 30 | class ChatRecordsFile extends Model 31 | { 32 | public $timestamps = false; 33 | 34 | /** 35 | * The table associated with the model. 36 | * 37 | * @var string 38 | */ 39 | protected $table = 'chat_records_file'; 40 | 41 | /** 42 | * The attributes that are mass assignable. 43 | * 44 | * @var array 45 | */ 46 | protected $fillable = ['id', 'record_id', 'user_id', 'file_source', 'file_type', 'save_type', 'original_name', 'file_suffix', 'file_size', 'save_dir', 'is_delete', 'created_at']; 47 | 48 | /** 49 | * The attributes that should be cast to native types. 50 | * 51 | * @var array 52 | */ 53 | protected $casts = ['id' => 'integer', 'record_id' => 'integer', 'user_id' => 'integer', 'file_source' => 'integer', 'file_type' => 'integer', 'save_type' => 'integer', 'file_size' => 'integer', 'is_delete' => 'integer', 'created_at' => 'datetime']; 54 | } 55 | -------------------------------------------------------------------------------- /app/Model/ChatRecordsForward.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $record_id 19 | * @property int $user_id 20 | * @property string $records_id 21 | * @property string $text 22 | * @property \Carbon\Carbon $created_at 23 | */ 24 | class ChatRecordsForward extends Model 25 | { 26 | /** 27 | * The table associated with the model. 28 | * 29 | * @var string 30 | */ 31 | protected $table = 'chat_records_forward'; 32 | 33 | /** 34 | * The attributes that are mass assignable. 35 | * 36 | * @var array 37 | */ 38 | protected $fillable = ['id', 'record_id', 'user_id', 'records_id', 'text', 'created_at']; 39 | 40 | /** 41 | * The attributes that should be cast to native types. 42 | * 43 | * @var array 44 | */ 45 | protected $casts = ['id' => 'integer', 'record_id' => 'integer', 'user_id' => 'integer', 'created_at' => 'datetime']; 46 | } 47 | -------------------------------------------------------------------------------- /app/Model/ChatRecordsInvite.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $record_id 19 | * @property int $type 20 | * @property int $operate_user_id 21 | * @property string $user_ids 22 | */ 23 | class ChatRecordsInvite extends Model 24 | { 25 | public $timestamps = false; 26 | 27 | /** 28 | * The table associated with the model. 29 | * 30 | * @var string 31 | */ 32 | protected $table = 'chat_records_invite'; 33 | 34 | /** 35 | * The attributes that are mass assignable. 36 | * 37 | * @var array 38 | */ 39 | protected $fillable = ['id', 'record_id', 'type', 'operate_user_id', 'user_ids']; 40 | 41 | /** 42 | * The attributes that should be cast to native types. 43 | * 44 | * @var array 45 | */ 46 | protected $casts = ['id' => 'integer', 'record_id' => 'integer', 'type' => 'integer', 'operate_user_id' => 'integer']; 47 | } 48 | -------------------------------------------------------------------------------- /app/Model/Emoticon.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | class Emoticon extends Model 17 | { 18 | /** 19 | * The table associated with the model. 20 | * 21 | * @var string 22 | */ 23 | protected $table = 'emoticon'; 24 | 25 | /** 26 | * The attributes that are mass assignable. 27 | * 28 | * @var array 29 | */ 30 | protected $fillable = []; 31 | 32 | /** 33 | * The attributes that should be cast to native types. 34 | * 35 | * @var array 36 | */ 37 | protected $casts = []; 38 | } 39 | -------------------------------------------------------------------------------- /app/Model/EmoticonDetail.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $emoticon_id 19 | * @property int $user_id 20 | * @property string $describe 21 | * @property string $url 22 | * @property string $file_suffix 23 | * @property int $file_size 24 | * @property \Carbon\Carbon $created_at 25 | */ 26 | class EmoticonDetail extends Model 27 | { 28 | public $timestamps = false; 29 | 30 | /** 31 | * The table associated with the model. 32 | * 33 | * @var string 34 | */ 35 | protected $table = 'emoticon_details'; 36 | 37 | /** 38 | * The attributes that are mass assignable. 39 | * 40 | * @var array 41 | */ 42 | protected $fillable = ['id', 'emoticon_id', 'user_id', 'describe', 'url', 'file_suffix', 'file_size', 'created_at']; 43 | 44 | /** 45 | * The attributes that should be cast to native types. 46 | * 47 | * @var array 48 | */ 49 | protected $casts = ['id' => 'integer', 'emoticon_id' => 'integer', 'user_id' => 'integer', 'file_size' => 'integer', 'created_at' => 'datetime']; 50 | } 51 | -------------------------------------------------------------------------------- /app/Model/FileSplitUpload.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $file_type 19 | * @property int $user_id 20 | * @property string $hash_name 21 | * @property string $original_name 22 | * @property int $split_index 23 | * @property int $split_num 24 | * @property string $save_dir 25 | * @property string $file_ext 26 | * @property int $file_size 27 | * @property int $is_delete 28 | * @property int $upload_at 29 | */ 30 | class FileSplitUpload extends Model 31 | { 32 | /** 33 | * @var bool 34 | */ 35 | public $timestamps = false; 36 | 37 | /** 38 | * The table associated with the model. 39 | * 40 | * @var string 41 | */ 42 | protected $table = 'file_split_upload'; 43 | 44 | /** 45 | * The attributes that are mass assignable. 46 | * 47 | * @var array 48 | */ 49 | protected $fillable = ['id', 'file_type', 'user_id', 'hash_name', 'original_name', 'split_index', 'split_num', 'save_dir', 'file_ext', 'file_size', 'is_delete', 'upload_at']; 50 | 51 | /** 52 | * The attributes that should be cast to native types. 53 | * 54 | * @var array 55 | */ 56 | protected $casts = ['id' => 'integer', 'file_type' => 'integer', 'user_id' => 'integer', 'split_index' => 'integer', 'split_num' => 'integer', 'file_size' => 'integer', 'is_delete' => 'integer', 'upload_at' => 'integer']; 57 | } 58 | -------------------------------------------------------------------------------- /app/Model/Model.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | use Hyperf\DbConnection\Model\Model as BaseModel; 17 | use Hyperf\ModelCache\Cacheable; 18 | use Hyperf\ModelCache\CacheableInterface; 19 | 20 | abstract class Model extends BaseModel implements CacheableInterface 21 | { 22 | use Cacheable; 23 | } 24 | -------------------------------------------------------------------------------- /app/Model/UserLoginLog.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property string $ip 20 | * @property \Carbon\Carbon $created_at 21 | */ 22 | class UserLoginLog extends Model 23 | { 24 | /** 25 | * The table associated with the model. 26 | * 27 | * @var string 28 | */ 29 | protected $table = 'user_login_log'; 30 | 31 | /** 32 | * The attributes that are mass assignable. 33 | * 34 | * @var array 35 | */ 36 | protected $fillable = ['id', 'user_id', 'ip', 'created_at']; 37 | 38 | /** 39 | * The attributes that should be cast to native types. 40 | * 41 | * @var array 42 | */ 43 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'created_at' => 'datetime']; 44 | } 45 | -------------------------------------------------------------------------------- /app/Model/Users.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property string $mobile 19 | * @property string $nickname 20 | * @property string $avatar 21 | * @property int $gender 22 | * @property string $password 23 | * @property string $invite_code 24 | * @property string $motto 25 | * @property string $email 26 | * @property \Carbon\Carbon $created_at 27 | * @property \Carbon\Carbon $updated_at 28 | */ 29 | class Users extends Model 30 | { 31 | /** 32 | * The table associated with the model. 33 | * 34 | * @var string 35 | */ 36 | protected $table = 'users'; 37 | 38 | /** 39 | * The attributes that are mass assignable. 40 | * 41 | * @var array 42 | */ 43 | protected $fillable = ['id', 'mobile', 'nickname', 'avatar', 'gender', 'password', 'invite_code', 'motto', 'email', 'created_at', 'updated_at']; 44 | 45 | /** 46 | * The attributes that should be cast to native types. 47 | * 48 | * @var array 49 | */ 50 | protected $casts = ['id' => 'integer', 'gender' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 51 | } 52 | -------------------------------------------------------------------------------- /app/Model/UsersEmoticon.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property string $emoticon_ids 20 | */ 21 | class UsersEmoticon extends Model 22 | { 23 | /** 24 | * The table associated with the model. 25 | * 26 | * @var string 27 | */ 28 | protected $table = 'users_emoticon'; 29 | 30 | /** 31 | * The attributes that are mass assignable. 32 | * 33 | * @var array 34 | */ 35 | protected $fillable = ['id', 'user_id', 'emoticon_ids']; 36 | 37 | /** 38 | * The attributes that should be cast to native types. 39 | * 40 | * @var array 41 | */ 42 | protected $casts = ['id' => 'integer', 'user_id' => 'integer']; 43 | } 44 | -------------------------------------------------------------------------------- /app/Model/UsersFriends.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | use Hyperf\DbConnection\Db; 17 | 18 | /** 19 | * @property int $id 20 | * @property int $user1 21 | * @property int $user2 22 | * @property string $user1_remark 23 | * @property string $user2_remark 24 | * @property int $active 25 | * @property int $status 26 | * @property string $agree_time 27 | * @property \Carbon\Carbon $created_at 28 | */ 29 | class UsersFriends extends Model 30 | { 31 | /** 32 | * The table associated with the model. 33 | * 34 | * @var string 35 | */ 36 | protected $table = 'users_friends'; 37 | 38 | /** 39 | * The attributes that are mass assignable. 40 | * 41 | * @var array 42 | */ 43 | protected $fillable = [ 44 | 'user1', 45 | 'user2', 46 | 'user1_remark', 47 | 'user2_remark', 48 | 'active', 49 | 'status', 50 | 'agree_time', 51 | 'created_at', 52 | ]; 53 | 54 | /** 55 | * The attributes that should be cast to native types. 56 | * 57 | * @var array 58 | */ 59 | protected $casts = ['id' => 'integer', 'user1' => 'integer', 'user2' => 'integer', 'active' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; 60 | 61 | /** 62 | * 获取用户所有好友. 63 | * 64 | * @param int $uid 用户ID 65 | * 66 | * @return mixed 67 | */ 68 | public static function getUserFriends(int $uid) 69 | { 70 | $sql = << $uid2) { 98 | [$uid1, $uid2] = [$uid2, $uid1]; 99 | } 100 | 101 | return self::where('user1', $uid1)->where('user2', $uid2)->where('status', 1)->exists(); 102 | } 103 | 104 | /** 105 | * 获取指定用户的所有朋友的用户ID. 106 | * 107 | * @param int $user_id 指定用户ID 108 | * @return array 109 | */ 110 | public static function getFriendIds(int $user_id) 111 | { 112 | $sql = "SELECT user2 as uid from im_users_friends where user1 = {$user_id} and `status` = 1 UNION all SELECT user1 as uid from im_users_friends where user2 = {$user_id} and `status` = 1"; 113 | return array_map(function ($item) { 114 | return $item->uid; 115 | }, Db::select($sql)); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/Model/UsersFriendsApply.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property int $friend_id 20 | * @property int $status 21 | * @property string $remarks 22 | * @property \Carbon\Carbon $created_at 23 | * @property \Carbon\Carbon $updated_at 24 | */ 25 | class UsersFriendsApply extends Model 26 | { 27 | /** 28 | * The table associated with the model. 29 | * 30 | * @var string 31 | */ 32 | protected $table = 'users_friends_apply'; 33 | 34 | /** 35 | * The attributes that are mass assignable. 36 | * 37 | * @var array 38 | */ 39 | protected $fillable = [ 40 | 'id', 41 | 'user_id', 42 | 'friend_id', 43 | 'status', 44 | 'remarks', 45 | ]; 46 | 47 | /** 48 | * The attributes that should be cast to native types. 49 | * 50 | * @var array 51 | */ 52 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'friend_id' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 53 | } 54 | -------------------------------------------------------------------------------- /app/Model/UsersGroup.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $user_id 19 | * @property string $group_name 20 | * @property string $group_profile 21 | * @property int $status 22 | * @property string $avatar 23 | * @property \Carbon\Carbon $created_at 24 | */ 25 | class UsersGroup extends Model 26 | { 27 | public $timestamps = false; 28 | 29 | /** 30 | * The table associated with the model. 31 | * 32 | * @var string 33 | */ 34 | protected $table = 'users_group'; 35 | 36 | /** 37 | * The attributes that are mass assignable. 38 | * 39 | * @var array 40 | */ 41 | protected $fillable = ['id', 'user_id', 'group_name', 'group_profile', 'status', 'avatar', 'created_at']; 42 | 43 | /** 44 | * The attributes that should be cast to native types. 45 | * 46 | * @var array 47 | */ 48 | protected $casts = ['id' => 'integer', 'user_id' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; 49 | 50 | /** 51 | * 判断用户是否是管理员. 52 | * 53 | * @param int $uid 用户ID 54 | * @param int $groupId 群ID 55 | * 56 | * @return mixed 57 | */ 58 | public static function isManager(int $uid, int $groupId) 59 | { 60 | return self::where('id', $groupId)->where('user_id', $uid)->exists(); 61 | } 62 | 63 | /** 64 | * 判断用户是否是群成员. 65 | * 66 | * @param int $groupId 群ID 67 | * @param int $uid 用户ID 68 | * 69 | * @return bool 70 | */ 71 | public static function isMember(int $groupId, int $uid) 72 | { 73 | return UsersGroupMember::where('group_id', $groupId)->where('user_id', $uid)->where('status', 0)->exists(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/Model/UsersGroupMember.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $group_id 19 | * @property int $user_id 20 | * @property int $group_owner 21 | * @property int $status 22 | * @property string $visit_card 23 | * @property \Carbon\Carbon $created_at 24 | */ 25 | class UsersGroupMember extends Model 26 | { 27 | public $timestamps = false; 28 | 29 | /** 30 | * The table associated with the model. 31 | * 32 | * @var string 33 | */ 34 | protected $table = 'users_group_member'; 35 | 36 | /** 37 | * The attributes that are mass assignable. 38 | * 39 | * @var array 40 | */ 41 | protected $fillable = ['id', 'group_id', 'user_id', 'group_owner', 'status', 'visit_card', 'created_at']; 42 | 43 | /** 44 | * The attributes that should be cast to native types. 45 | * 46 | * @var array 47 | */ 48 | protected $casts = ['id' => 'integer', 'group_id' => 'integer', 'user_id' => 'integer', 'group_owner' => 'integer', 'status' => 'integer', 'created_at' => 'datetime']; 49 | 50 | /** 51 | * 获取聊天群成员ID. 52 | * 53 | * @return mixed 54 | */ 55 | public static function getGroupMemberIds(int $groupId) 56 | { 57 | return self::where('group_id', $groupId)->where('status', 0)->pluck('user_id')->toArray(); 58 | } 59 | 60 | /** 61 | * 获取用户的群名片. 62 | * 63 | * @param int $user_id 用户ID 64 | * @param int $group_id 群ID 65 | * @return mixed 66 | */ 67 | public static function visitCard(int $user_id, int $group_id) 68 | { 69 | return self::where('group_id', $group_id)->where('user_id', $user_id)->value('visit_card'); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/Model/UsersGroupNotice.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Model; 15 | 16 | /** 17 | * @property int $id 18 | * @property int $group_id 19 | * @property int $user_id 20 | * @property string $title 21 | * @property string $content 22 | * @property int $is_delete 23 | * @property \Carbon\Carbon $created_at 24 | * @property \Carbon\Carbon $updated_at 25 | * @property string $deleted_at 26 | */ 27 | class UsersGroupNotice extends Model 28 | { 29 | /** 30 | * The table associated with the model. 31 | * 32 | * @var string 33 | */ 34 | protected $table = 'users_group_notice'; 35 | 36 | /** 37 | * The attributes that are mass assignable. 38 | * 39 | * @var array 40 | */ 41 | protected $fillable = ['id', 'group_id', 'user_id', 'title', 'content', 'is_delete', 'created_at', 'updated_at', 'deleted_at']; 42 | 43 | /** 44 | * The attributes that should be cast to native types. 45 | * 46 | * @var array 47 | */ 48 | protected $casts = ['id' => 'integer', 'group_id' => 'integer', 'user_id' => 'integer', 'is_delete' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 49 | } 50 | -------------------------------------------------------------------------------- /app/Process/AsyncQueueConsumer.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Process; 15 | 16 | use Hyperf\AsyncQueue\Process\ConsumerProcess; 17 | use Hyperf\Process\Annotation\Process; 18 | 19 | /** 20 | * @Process(name="async-queue") 21 | */ 22 | class AsyncQueueConsumer extends ConsumerProcess 23 | { 24 | } 25 | -------------------------------------------------------------------------------- /app/Service/Traits/PagingTrait.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace App\Service\Traits; 15 | 16 | /** 17 | * Trait PagingTrait 分页处理. 18 | */ 19 | trait PagingTrait 20 | { 21 | /** 22 | * 计算分页总数. 23 | * 24 | * @param int $total 总记录数 25 | * @param int $page_size 分页大小 26 | * 27 | * @return int 分页总数 28 | */ 29 | protected function getPagingTotal(int $total, int $page_size) 30 | { 31 | return ($total === 0) ? 0 : (int) ceil((int) $total / (int) $page_size); 32 | } 33 | 34 | /** 35 | * 获取分页数据. 36 | * 37 | * @param array $rows 列表数据 38 | * @param int $total 数据总记录数 39 | * @param int $page 当前分页 40 | * @param int $page_size 分页大小 41 | * @param array $params 额外参数 42 | * 43 | * @return array 44 | */ 45 | protected function getPagingRows(array $rows, int $total, int $page, int $page_size, array $params = []) 46 | { 47 | return array_merge([ 48 | 'rows' => $rows, 49 | 'page' => $page, 50 | 'page_size' => $page_size, 51 | 'page_total' => ($page_size == 0) ? 1 : $this->getPagingTotal($total, $page_size), 52 | 'total' => $total, 53 | ], $params); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /bin/hyperf.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | get(\Hyperf\Contract\ApplicationInterface::class); 22 | $application->run(); 23 | })(); 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperf/hyperf-skeleton", 3 | "type": "project", 4 | "keywords": [ 5 | "php", 6 | "swoole", 7 | "framework", 8 | "hyperf", 9 | "microservice", 10 | "middleware" 11 | ], 12 | "description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.", 13 | "license": "Apache-2.0", 14 | "require": { 15 | "php": ">=7.3", 16 | "ext-dom": "*", 17 | "ext-json": "*", 18 | "ext-pdo": "*", 19 | "ext-redis": "*", 20 | "ext-swoole": ">=4.5", 21 | "ext-zip": "*", 22 | "codedungeon/php-cli-colors": "^1.12", 23 | "hyperf/amqp": "2.1.*", 24 | "hyperf/async-queue": "2.1.*", 25 | "hyperf/cache": "2.1.*", 26 | "hyperf/command": "2.1.*", 27 | "hyperf/config": "2.1.*", 28 | "hyperf/constants": "2.1.*", 29 | "hyperf/consul": "2.1.*", 30 | "hyperf/database": "2.1.*", 31 | "hyperf/db-connection": "2.1.*", 32 | "hyperf/elasticsearch": "2.1.*", 33 | "hyperf/engine": "^1.0", 34 | "hyperf/filesystem": "2.1.*", 35 | "hyperf/framework": "2.1.*", 36 | "hyperf/guzzle": "2.1.*", 37 | "hyperf/http-server": "2.1.*", 38 | "hyperf/json-rpc": "2.1.*", 39 | "hyperf/logger": "2.1.*", 40 | "hyperf/memory": "2.1.*", 41 | "hyperf/model-cache": "2.1.*", 42 | "hyperf/nsq": "2.1.*", 43 | "hyperf/process": "2.1.*", 44 | "hyperf/rate-limit": "2.1.*", 45 | "hyperf/redis": "2.1.*", 46 | "hyperf/rpc": "2.1.*", 47 | "hyperf/rpc-client": "2.1.*", 48 | "hyperf/rpc-server": "2.1.*", 49 | "hyperf/service-governance": "2.1.*", 50 | "hyperf/socketio-server": "2.1.*", 51 | "hyperf/task": "2.1.*", 52 | "hyperf/tracer": "2.1.*", 53 | "hyperf/view": "2.1.*", 54 | "hyperf/view-engine": "2.1.*", 55 | "hyperf/websocket-client": "2.1.*", 56 | "hyperf/websocket-server": "2.1.*", 57 | "overtrue/flysystem-qiniu": "^1.0", 58 | "phper666/jwt-auth": "^3.0", 59 | "phpmailer/phpmailer": "^6.1" 60 | }, 61 | "require-dev": { 62 | "roave/security-advisories": "dev-master", 63 | "swoole/ide-helper": "^4.5", 64 | "friendsofphp/php-cs-fixer": "^2.14", 65 | "mockery/mockery": "^1.0", 66 | "phpstan/phpstan": "^0.12", 67 | "hyperf/devtool": "2.1.*", 68 | "hyperf/testing": "2.1.*", 69 | "symfony/var-dumper": "^5.1" 70 | }, 71 | "suggest": { 72 | "ext-openssl": "Required to use HTTPS.", 73 | "ext-json": "Required to use JSON.", 74 | "ext-pdo": "Required to use MySQL Client.", 75 | "ext-pdo_mysql": "Required to use MySQL Client.", 76 | "ext-redis": "Required to use Redis Client." 77 | }, 78 | "autoload": { 79 | "psr-4": { 80 | "App\\": "app/" 81 | }, 82 | "files": [ 83 | "app/Kernel/Functions.php" 84 | ] 85 | }, 86 | "autoload-dev": { 87 | "psr-4": { 88 | "HyperfTest\\": "./test/" 89 | } 90 | }, 91 | "minimum-stability": "dev", 92 | "prefer-stable": true, 93 | "config": { 94 | "optimize-autoloader": true, 95 | "sort-packages": true 96 | }, 97 | "extra": [], 98 | "scripts": { 99 | "post-root-package-install": [ 100 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 101 | ], 102 | "post-autoload-dump": [ 103 | "rm -rf runtime/container" 104 | ], 105 | "test": "co-phpunit -c phpunit.xml --colors=always", 106 | "cs-fix": "php-cs-fixer fix $1", 107 | "analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config", 108 | "start": "php ./bin/hyperf.php start" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /config/autoload/amqp.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'host' => env('AMQP_HOST', 'localhost'), 17 | 'port' => 5672, 18 | 'user' => 'guest', 19 | 'password' => 'guest', 20 | 'vhost' => '/', 21 | 'concurrent' => [ 22 | 'limit' => 1, 23 | ], 24 | 'pool' => [ 25 | 'min_connections' => 1, 26 | 'max_connections' => 10, 27 | 'connect_timeout' => 10.0, 28 | 'wait_timeout' => 3.0, 29 | 'heartbeat' => -1, 30 | ], 31 | 'params' => [ 32 | 'insist' => false, 33 | 'login_method' => 'AMQPLAIN', 34 | 'login_response' => null, 35 | 'locale' => 'en_US', 36 | 'connection_timeout' => 3.0, 37 | 'read_write_timeout' => 6.0, 38 | 'context' => null, 39 | 'keepalive' => true, 40 | 'heartbeat' => 3, 41 | 'close_on_destruct' => false, 42 | ], 43 | ], 44 | ]; 45 | -------------------------------------------------------------------------------- /config/autoload/annotations.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use Hyperf\Utils\Coroutine; 15 | 16 | return [ 17 | 'scan' => [ 18 | 'paths' => [ 19 | BASE_PATH . '/app', 20 | ], 21 | 'ignore_annotations' => [ 22 | 'mixin', 23 | ], 24 | 'class_map' => [ 25 | // 需要映射的类名 => 类所在的文件地址 26 | Coroutine::class => BASE_PATH . '/app/Kernel/ClassMap/Coroutine.php', 27 | ], 28 | ], 29 | ]; 30 | -------------------------------------------------------------------------------- /config/autoload/aspects.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | ]; 16 | -------------------------------------------------------------------------------- /config/autoload/async_queue.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'driver' => Hyperf\AsyncQueue\Driver\RedisDriver::class, 17 | 'redis' => [ 18 | 'pool' => 'default', 19 | ], 20 | 'channel' => 'queue', 21 | 'timeout' => 2, 22 | 'retry_seconds' => 5, 23 | 'handle_timeout' => 10, 24 | 'processes' => 1, 25 | 'concurrent' => [ 26 | 'limit' => 5, 27 | ], 28 | ], 29 | ]; 30 | -------------------------------------------------------------------------------- /config/autoload/cache.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'driver' => Hyperf\Cache\Driver\RedisDriver::class, 17 | 'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class, 18 | 'prefix' => 'c:', 19 | ], 20 | ]; 21 | -------------------------------------------------------------------------------- /config/autoload/commands.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use App\Command\SocketIOClear; 15 | 16 | return [ 17 | SocketIOClear::class, 18 | ]; 19 | -------------------------------------------------------------------------------- /config/autoload/consul.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'uri' => env('CONSUL_HOST', 'http://127.0.0.1:8500'), 16 | ]; 17 | -------------------------------------------------------------------------------- /config/autoload/databases.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'driver' => env('DB_DRIVER', 'mysql'), 17 | 'host' => env('DB_HOST', 'localhost'), 18 | 'port' => env('DB_PORT', 3306), 19 | 'database' => env('DB_DATABASE', 'hyperf'), 20 | 'username' => env('DB_USERNAME', 'root'), 21 | 'password' => env('DB_PASSWORD', ''), 22 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 23 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 24 | 'prefix' => env('DB_PREFIX', ''), 25 | 'pool' => [ 26 | 'min_connections' => 1, 27 | 'max_connections' => 10, 28 | 'connect_timeout' => 10.0, 29 | 'wait_timeout' => 3.0, 30 | 'heartbeat' => -1, 31 | 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60), 32 | ], 33 | 'cache' => [ 34 | 'handler' => Hyperf\ModelCache\Handler\RedisHandler::class, 35 | 'cache_key' => '{mc:%s:m:%s}:%s:%s', 36 | 'prefix' => 'default', 37 | 'ttl' => 3600 * 24, 38 | 'empty_model_ttl' => 600, 39 | 'load_script' => true, 40 | ], 41 | 'commands' => [ 42 | 'gen:model' => [ 43 | 'path' => 'app/Model', 44 | 'force_casts' => true, 45 | 'inheritance' => 'Model', 46 | 'refresh_fillable' => true, 47 | 'uses' => '', 48 | 'table_mapping' => [], 49 | ], 50 | ], 51 | ], 52 | ]; 53 | -------------------------------------------------------------------------------- /config/autoload/db.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'driver' => 'pdo', 17 | 'host' => env('DB_HOST', 'localhost'), 18 | 'port' => env('DB_PORT', 3306), 19 | 'database' => env('DB_DATABASE', 'hyperf'), 20 | 'username' => env('DB_USERNAME', 'root'), 21 | 'password' => env('DB_PASSWORD', ''), 22 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 23 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 24 | 'fetch_mode' => PDO::FETCH_ASSOC, 25 | 'pool' => [ 26 | 'min_connections' => 1, 27 | 'max_connections' => 10, 28 | 'connect_timeout' => 10.0, 29 | 'wait_timeout' => 3.0, 30 | 'heartbeat' => -1, 31 | 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60), 32 | ], 33 | 'options' => [ 34 | PDO::ATTR_CASE => PDO::CASE_NATURAL, 35 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 36 | PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, 37 | PDO::ATTR_STRINGIFY_FETCHES => false, 38 | PDO::ATTR_EMULATE_PREPARES => false, 39 | ], 40 | ], 41 | ]; 42 | -------------------------------------------------------------------------------- /config/autoload/dependencies.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use App\Kernel\SocketIO\SocketIO as KernelSocketIO; 15 | use Hyperf\JsonRpc\JsonRpcPoolTransporter; 16 | use Hyperf\JsonRpc\JsonRpcTransporter; 17 | use Hyperf\SocketIOServer\Room\AdapterInterface; 18 | use Hyperf\SocketIOServer\Room\RedisNsqAdapter; 19 | use Hyperf\SocketIOServer\SocketIO; 20 | use Hyperf\Utils\Serializer\Serializer; 21 | use Hyperf\Utils\Serializer\SerializerFactory; 22 | 23 | return [ 24 | AdapterInterface::class => RedisNsqAdapter::class, 25 | SocketIO::class => KernelSocketIO::class, 26 | JsonRpcTransporter::class => JsonRpcPoolTransporter::class, 27 | Hyperf\Contract\NormalizerInterface::class => new SerializerFactory(Serializer::class), 28 | ]; 29 | -------------------------------------------------------------------------------- /config/autoload/devtool.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'generator' => [ 16 | 'amqp' => [ 17 | 'consumer' => [ 18 | 'namespace' => 'App\\Amqp\\Consumer', 19 | ], 20 | 'producer' => [ 21 | 'namespace' => 'App\\Amqp\\Producer', 22 | ], 23 | ], 24 | 'aspect' => [ 25 | 'namespace' => 'App\\Aspect', 26 | ], 27 | 'command' => [ 28 | 'namespace' => 'App\\Command', 29 | ], 30 | 'controller' => [ 31 | 'namespace' => 'App\\Controller', 32 | ], 33 | 'job' => [ 34 | 'namespace' => 'App\\Job', 35 | ], 36 | 'listener' => [ 37 | 'namespace' => 'App\\Listener', 38 | ], 39 | 'middleware' => [ 40 | 'namespace' => 'App\\Middleware', 41 | ], 42 | 'Process' => [ 43 | 'namespace' => 'App\\Processes', 44 | ], 45 | ], 46 | ]; 47 | -------------------------------------------------------------------------------- /config/autoload/exceptions.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'handler' => [ 16 | 'http' => [ 17 | Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class, 18 | App\Exception\Handler\AppExceptionHandler::class, 19 | ], 20 | ], 21 | ]; 22 | -------------------------------------------------------------------------------- /config/autoload/file.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use Hyperf\Filesystem\Adapter\AliyunOssAdapterFactory; 15 | use Hyperf\Filesystem\Adapter\CosAdapterFactory; 16 | use Hyperf\Filesystem\Adapter\FtpAdapterFactory; 17 | use Hyperf\Filesystem\Adapter\LocalAdapterFactory; 18 | use Hyperf\Filesystem\Adapter\MemoryAdapterFactory; 19 | use Hyperf\Filesystem\Adapter\QiniuAdapterFactory; 20 | use Hyperf\Filesystem\Adapter\S3AdapterFactory; 21 | 22 | return [ 23 | 'default' => 'local', 24 | 'storage' => [ 25 | 'local' => [ 26 | 'driver' => LocalAdapterFactory::class, 27 | 'root' => BASE_PATH . '/public', 28 | ], 29 | 'ftp' => [ 30 | 'driver' => FtpAdapterFactory::class, 31 | 'host' => 'ftp.example.com', 32 | 'username' => 'username', 33 | 'password' => 'password', 34 | // 'port' => 21, 35 | // 'root' => '/path/to/root', 36 | // 'passive' => true, 37 | // 'ssl' => true, 38 | // 'timeout' => 30, 39 | // 'ignorePassiveAddress' => false, 40 | ], 41 | 'memory' => [ 42 | 'driver' => MemoryAdapterFactory::class, 43 | ], 44 | 's3' => [ 45 | 'driver' => S3AdapterFactory::class, 46 | 'credentials' => [ 47 | 'key' => env('S3_KEY'), 48 | 'secret' => env('S3_SECRET'), 49 | ], 50 | 'region' => env('S3_REGION'), 51 | 'version' => 'latest', 52 | 'bucket_endpoint' => false, 53 | 'use_path_style_endpoint' => false, 54 | 'endpoint' => env('S3_ENDPOINT'), 55 | 'bucket_name' => env('S3_BUCKET'), 56 | ], 57 | 'minio' => [ 58 | 'driver' => S3AdapterFactory::class, 59 | 'credentials' => [ 60 | 'key' => env('S3_KEY'), 61 | 'secret' => env('S3_SECRET'), 62 | ], 63 | 'region' => env('S3_REGION'), 64 | 'version' => 'latest', 65 | 'bucket_endpoint' => false, 66 | 'use_path_style_endpoint' => true, 67 | 'endpoint' => env('S3_ENDPOINT'), 68 | 'bucket_name' => env('S3_BUCKET'), 69 | ], 70 | 'oss' => [ 71 | 'driver' => AliyunOssAdapterFactory::class, 72 | 'accessId' => env('OSS_ACCESS_ID'), 73 | 'accessSecret' => env('OSS_ACCESS_SECRET'), 74 | 'bucket' => env('OSS_BUCKET'), 75 | 'endpoint' => env('OSS_ENDPOINT'), 76 | // 'timeout' => 3600, 77 | // 'connectTimeout' => 10, 78 | // 'isCName' => false, 79 | // 'token' => '', 80 | ], 81 | 'qiniu' => [ 82 | 'driver' => QiniuAdapterFactory::class, 83 | 'accessKey' => env('QINIU_ACCESS_KEY'), 84 | 'secretKey' => env('QINIU_SECRET_KEY'), 85 | 'bucket' => env('QINIU_BUCKET'), 86 | 'domain' => env('QINBIU_DOMAIN'), 87 | ], 88 | 'cos' => [ 89 | 'driver' => CosAdapterFactory::class, 90 | 'region' => env('COS_REGION'), 91 | 'credentials' => [ 92 | 'appId' => env('COS_APPID'), 93 | 'secretId' => env('COS_SECRET_ID'), 94 | 'secretKey' => env('COS_SECRET_KEY'), 95 | ], 96 | 'bucket' => env('COS_BUCKET'), 97 | 'read_from_cdn' => false, 98 | // 'timeout' => 60, 99 | // 'connect_timeout' => 60, 100 | // 'cdn' => '', 101 | // 'scheme' => 'https', 102 | ], 103 | ], 104 | ]; 105 | -------------------------------------------------------------------------------- /config/autoload/jwt.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use Lcobucci\JWT\Signer\Hmac\Sha256; 15 | use Lcobucci\JWT\Signer\Hmac\Sha384; 16 | use Lcobucci\JWT\Signer\Hmac\Sha512; 17 | 18 | /* 19 | * @TODO 重新封装JWT组件,现在的组件有许多不确定问题 20 | */ 21 | return [ 22 | 'login_type' => env('JWT_LOGIN_TYPE', 'sso'), // 登录方式,sso为单点登录,mpop为多点登录 23 | 24 | /* 25 | * 单点登录自定义数据中必须存在uid的键值,这个key你可以自行定义,只要自定义数据中存在该键即可 26 | */ 27 | 'sso_key' => 'uid', 28 | 29 | 'secret' => env('JWT_SECRET', 'Hyperf-Chat'), // 非对称加密使用字符串,请使用自己加密的字符串 30 | 31 | /* 32 | * JWT 权限keys 33 | * 对称算法: HS256, HS384 & HS512 使用 `JWT_SECRET`. 34 | * 非对称算法: RS256, RS384 & RS512 / ES256, ES384 & ES512 使用下面的公钥私钥. 35 | */ 36 | 'keys' => [ 37 | 'public' => env('JWT_PUBLIC_KEY'), // 公钥,例如:'file:///path/to/public/key' 38 | 'private' => env('JWT_PRIVATE_KEY'), // 私钥,例如:'file:///path/to/private/key' 39 | ], 40 | 41 | 'ttl' => env('JWT_TTL', 7200), // token过期时间,单位为秒 42 | 43 | 'alg' => env('JWT_ALG', 'HS256'), // jwt的hearder加密算法 44 | 45 | /* 46 | * 支持的算法 47 | */ 48 | 'supported_algs' => [ 49 | 'HS256' => Sha256::class, 50 | 'HS384' => Sha384::class, 51 | 'HS512' => Sha512::class, 52 | 'ES256' => \Lcobucci\JWT\Signer\Ecdsa\Sha256::class, 53 | 'ES384' => \Lcobucci\JWT\Signer\Ecdsa\Sha384::class, 54 | 'ES512' => \Lcobucci\JWT\Signer\Ecdsa\Sha512::class, 55 | 'RS256' => \Lcobucci\JWT\Signer\Rsa\Sha256::class, 56 | 'RS384' => \Lcobucci\JWT\Signer\Rsa\Sha384::class, 57 | 'RS512' => \Lcobucci\JWT\Signer\Rsa\Sha512::class, 58 | ], 59 | 60 | /* 61 | * 对称算法名称 62 | */ 63 | 'symmetry_algs' => [ 64 | 'HS256', 65 | 'HS384', 66 | 'HS512', 67 | ], 68 | 69 | /* 70 | * 非对称算法名称 71 | */ 72 | 'asymmetric_algs' => [ 73 | 'RS256', 74 | 'RS384', 75 | 'RS512', 76 | 'ES256', 77 | 'ES384', 78 | 'ES512', 79 | ], 80 | 81 | /* 82 | * 是否开启黑名单,单点登录和多点登录的注销、刷新使原token失效,必须要开启黑名单,目前黑名单缓存只支持hyperf缓存驱动 83 | */ 84 | 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), 85 | 86 | /* 87 | * 黑名单的宽限时间 单位为:秒,注意:如果使用单点登录,该宽限时间无效 88 | */ 89 | 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), 90 | 91 | /* 92 | * 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为1天,最好设置跟过期时间一样 93 | */ 94 | 'blacklist_cache_ttl' => env('JWT_TTL', 86400), 95 | 96 | 'blacklist_prefix' => 'cloud_jwt', // 黑名单缓存的前缀 97 | 98 | 'scene' => [ 99 | 'default' => [], 100 | 'cloud' => [ 101 | 'secret' => 'cloud', // 非对称加密使用字符串,请使用自己加密的字符串 102 | 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 103 | 'sso_key' => 'cloud_uid', 104 | 'ttl' => 7200, // token过期时间,单位为秒 105 | 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 106 | ], 107 | ], 108 | ]; 109 | -------------------------------------------------------------------------------- /config/autoload/listeners.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use App\Listener\LoginAfterListener; 15 | use App\Listener\OnShutdownListener; 16 | use App\Listener\OnStartListener; 17 | use App\Listener\OnWorkerStopListener; 18 | 19 | return [ 20 | OnStartListener::class, 21 | OnWorkerStopListener::class, 22 | OnShutdownListener::class, 23 | LoginAfterListener::class, 24 | ]; 25 | -------------------------------------------------------------------------------- /config/autoload/logger.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use App\Kernel\Log\AppendRequestIdProcessor; 15 | use Monolog\Formatter; 16 | 17 | return [ 18 | 'default' => [ 19 | 'handler' => [ 20 | 'class' => Monolog\Handler\StreamHandler::class, 21 | 'constructor' => [ 22 | 'stream' => BASE_PATH . '/runtime/logs/hyperf.log', 23 | 'level' => Monolog\Logger::DEBUG, 24 | ], 25 | ], 26 | 'formatter' => [ 27 | 'class' => Formatter\JsonFormatter::class, 28 | 'constructor' => [ 29 | 'batchMode' => Formatter\JsonFormatter::BATCH_MODE_JSON, 30 | 'appendNewline' => true, 31 | ], 32 | ], 33 | 'processors' => [ 34 | [ 35 | 'class' => AppendRequestIdProcessor::class, 36 | ], 37 | ], 38 | ], 39 | 'sql' => [ 40 | 'handler' => [ 41 | 'class' => Monolog\Handler\RotatingFileHandler::class, 42 | 'constructor' => [ 43 | 'filename' => BASE_PATH . '/runtime/logs/sql/sql.log', 44 | 'level' => Monolog\Logger::DEBUG, 45 | ], 46 | ], 47 | 'formatter' => [ 48 | 'class' => Monolog\Formatter\LineFormatter::class, 49 | 'constructor' => [ 50 | 'format' => null, 51 | 'dateFormat' => 'Y-m-d H:i:s', 52 | 'allowInlineLineBreaks' => true, 53 | ], 54 | ], 55 | 'processors' => [ 56 | [ 57 | 'class' => AppendRequestIdProcessor::class, 58 | ], 59 | ], 60 | ], 61 | ]; 62 | -------------------------------------------------------------------------------- /config/autoload/mail.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | /* 16 | |-------------------------------------------------------------------------- 17 | | SMTP Host Address 18 | |-------------------------------------------------------------------------- 19 | | 20 | | Here you may provide the host address of the SMTP server used by your 21 | | applications. A default option is provided that is compatible with 22 | | the Mailgun mail service which will provide reliable deliveries. 23 | | 24 | */ 25 | 26 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | SMTP Host Port 31 | |-------------------------------------------------------------------------- 32 | | 33 | | This is the SMTP port used by your application to deliver e-mails to 34 | | users of the application. Like the host we have set this value to 35 | | stay compatible with the Mailgun e-mail application by default. 36 | | 37 | */ 38 | 39 | 'port' => env('MAIL_PORT', 587), 40 | 41 | /* 42 | |-------------------------------------------------------------------------- 43 | | SMTP Server Username 44 | |-------------------------------------------------------------------------- 45 | | 46 | | If your SMTP server requires a username for authentication, you should 47 | | set it here. This will get used to authenticate with your server on 48 | | connection. You may also set the "password" value below this one. 49 | | 50 | */ 51 | 52 | 'username' => env('MAIL_USERNAME'), 53 | 54 | 'password' => env('MAIL_PASSWORD'), 55 | 56 | 'from' => env('MAIL_FROM'), 57 | 58 | 'name' => env('MAIL_NAME'), 59 | ]; 60 | -------------------------------------------------------------------------------- /config/autoload/middlewares.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use App\Middleware\CorsMiddleware; 15 | use App\Middleware\SocketIOAuthMiddleware; 16 | 17 | return [ 18 | 'http' => [ 19 | CorsMiddleware::class, 20 | ], 21 | 'socket-io' => [ 22 | SocketIOAuthMiddleware::class, 23 | ], 24 | ]; 25 | -------------------------------------------------------------------------------- /config/autoload/nsq.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'enable' => true, 17 | 'host' => env('NSQ_HOST', 'localhost'), 18 | 'port' => 4150, 19 | 'pool' => [ 20 | 'min_connections' => 1, 21 | 'max_connections' => 10, 22 | 'connect_timeout' => 10.0, 23 | 'wait_timeout' => 3.0, 24 | 'heartbeat' => -1, 25 | 'max_idle_time' => 60.0, 26 | ], 27 | 'nsqd' => [ 28 | 'port' => 4151, 29 | 'options' => [ 30 | ], 31 | ], 32 | ], 33 | ]; 34 | -------------------------------------------------------------------------------- /config/autoload/opentracing.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use Hyperf\Tracer\Adapter\JaegerTracerFactory; 15 | use Zipkin\Samplers\BinarySampler; 16 | 17 | return [ 18 | 'default' => env('TRACER_DRIVER', 'zipkin'), 19 | 'enable' => [ 20 | 'guzzle' => env('TRACER_ENABLE_GUZZLE', false), 21 | 'redis' => env('TRACER_ENABLE_REDIS', false), 22 | 'db' => env('TRACER_ENABLE_DB', false), 23 | 'method' => env('TRACER_ENABLE_METHOD', false), 24 | ], 25 | 'tracer' => [ 26 | 'zipkin' => [ 27 | 'driver' => \Hyperf\Tracer\Adapter\ZipkinTracerFactory::class, 28 | 'app' => [ 29 | 'name' => env('APP_NAME', 'skeleton'), 30 | // Hyperf will detect the system info automatically as the value if ipv4, ipv6, port is null 31 | 'ipv4' => '127.0.0.1', 32 | 'ipv6' => null, 33 | 'port' => 9501, 34 | ], 35 | 'options' => [ 36 | 'endpoint_url' => env('ZIPKIN_ENDPOINT_URL', 'http://localhost:9411/api/v2/spans'), 37 | 'timeout' => env('ZIPKIN_TIMEOUT', 1), 38 | ], 39 | 'sampler' => BinarySampler::createAsAlwaysSample(), 40 | ], 41 | 'jaeger' => [ 42 | 'driver' => JaegerTracerFactory::class, 43 | 'name' => env('APP_NAME', 'skeleton'), 44 | 'options' => [ 45 | 'local_agent' => [ 46 | 'reporting_host' => env('JAEGER_REPORTING_HOST', 'localhost'), 47 | 'reporting_port' => env('JAEGER_REPORTING_PORT', 5775), 48 | ], 49 | ], 50 | ], 51 | ], 52 | ]; 53 | -------------------------------------------------------------------------------- /config/autoload/processes.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | ]; 16 | -------------------------------------------------------------------------------- /config/autoload/rate_limit.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'create' => 1, 16 | 'consume' => 1, 17 | 'capacity' => 2, 18 | 'limitCallback' => [], 19 | 'waitTimeout' => 1, 20 | ]; 21 | -------------------------------------------------------------------------------- /config/autoload/redis.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'default' => [ 16 | 'host' => env('REDIS_HOST', 'localhost'), 17 | 'auth' => env('REDIS_AUTH', null), 18 | 'port' => (int) env('REDIS_PORT', 6379), 19 | 'db' => (int) env('REDIS_DB', 0), 20 | 'pool' => [ 21 | 'min_connections' => 1, 22 | 'max_connections' => 10, 23 | 'connect_timeout' => 10.0, 24 | 'wait_timeout' => 3.0, 25 | 'heartbeat' => -1, 26 | 'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60), 27 | ], 28 | ], 29 | ]; 30 | -------------------------------------------------------------------------------- /config/autoload/server.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | 15 | use Hyperf\Server\Event; 16 | use Hyperf\Server\Server; 17 | 18 | return [ 19 | 'mode' => SWOOLE_PROCESS, 20 | 'servers' => [ 21 | [ 22 | 'name' => 'jsonrpc', 23 | 'type' => Server::SERVER_BASE, 24 | 'host' => '0.0.0.0', 25 | 'port' => 9504, 26 | 'sock_type' => SWOOLE_SOCK_TCP, 27 | 'callbacks' => [ 28 | Event::ON_RECEIVE => [Hyperf\JsonRpc\TcpServer::class, 'onReceive'], 29 | ], 30 | 'settings' => [ 31 | 'open_length_check' => true, 32 | 'package_length_type' => 'N', 33 | 'package_length_offset' => 0, 34 | 'package_body_offset' => 4, 35 | 'package_max_length' => 1024 * 1024 * 2, 36 | ], 37 | ], 38 | [ 39 | 'name' => 'http', 40 | 'type' => Server::SERVER_HTTP, 41 | 'host' => '0.0.0.0', 42 | 'port' => 9500, 43 | 'sock_type' => SWOOLE_SOCK_TCP, 44 | 'settings' => [ 45 | 'package_max_length' => 1024 * 1024 * 4, 46 | ], 47 | 'callbacks' => [ 48 | Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'], 49 | ], 50 | ], 51 | [ 52 | 'name' => 'socket-io', 53 | 'type' => Server::SERVER_WEBSOCKET, 54 | 'host' => '0.0.0.0', 55 | 'port' => 9502, 56 | 'sock_type' => SWOOLE_SOCK_TCP, 57 | 'callbacks' => [ 58 | Event::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'], 59 | Event::ON_MESSAGE => [Hyperf\WebSocketServer\Server::class, 'onMessage'], 60 | Event::ON_CLOSE => [Hyperf\WebSocketServer\Server::class, 'onClose'], 61 | ], 62 | ], 63 | ], 64 | 'settings' => [ 65 | 'enable_coroutine' => true, 66 | 'worker_num' => 1, 67 | 'pid_file' => BASE_PATH . '/runtime/hyperf.pid', 68 | 'open_tcp_nodelay' => true, 69 | 'max_coroutine' => 100000, 70 | 'open_http2_protocol' => true, 71 | 'max_request' => 100000, 72 | 'socket_buffer_size' => 2 * 1024 * 1024, 73 | 'buffer_output_size' => 2 * 1024 * 1024, 74 | // Task Worker 数量,根据您的服务器配置而配置适当的数量 75 | 'task_worker_num' => 1, 76 | // 因为 `Task` 主要处理无法协程化的方法,所以这里推荐设为 `false`,避免协程下出现数据混淆的情况 77 | 'task_enable_coroutine' => true, 78 | // 将 public 替换为上传目录 79 | 'document_root' => BASE_PATH . '/public', 80 | 'enable_static_handler' => true, 81 | ], 82 | 'callbacks' => [ 83 | Event::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'], 84 | Event::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'], 85 | Event::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'], 86 | Event::ON_TASK => [Hyperf\Framework\Bootstrap\TaskCallback::class, 'onTask'], 87 | Event::ON_FINISH => [Hyperf\Framework\Bootstrap\FinishCallback::class, 'onFinish'], 88 | Event::ON_SHUTDOWN => [Hyperf\Framework\Bootstrap\ShutdownCallback::class, 'onShutdown'], 89 | ], 90 | ]; 91 | -------------------------------------------------------------------------------- /config/autoload/services.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use App\JsonRpc\Contract\InterfaceGroupService; 15 | use App\JsonRpc\Contract\InterfaceUserService; 16 | 17 | return [ 18 | 'consumers' => value(static function () { 19 | $consumers = []; 20 | // 这里示例自动创建代理消费者类的配置形式,顾存在 name 和 service 两个配置项,这里的做法不是唯一的,仅说明可以通过 PHP 代码来生成配置 21 | // 下面的 FooServiceInterface 和 BarServiceInterface 仅示例多服务,并不是在文档示例中真实存在的 22 | $services = [ 23 | 'UserService' => InterfaceUserService::class, 24 | 'GroupService' => InterfaceGroupService::class, 25 | ]; 26 | foreach ($services as $name => $interface) { 27 | $consumers[] = [ 28 | 'name' => $name, 29 | 'service' => $interface, 30 | 'registry' => [ 31 | 'protocol' => 'consul', 32 | 'address' => env('CONSUL_HOST', 'http://127.0.0.1:8500'), 33 | ], 34 | 'id' => $interface, 35 | // 服务提供者的服务协议,可选,默认值为 jsonrpc-http 36 | // 可选 jsonrpc-http jsonrpc jsonrpc-tcp-length-check 37 | 'protocol' => 'jsonrpc-tcp-length-check', 38 | // 负载均衡算法,可选,默认值为 random 39 | 'load_balancer' => 'random', 40 | // 这个消费者要从哪个服务中心获取节点信息,如不配置则不会从服务中心获取节点信息 41 | // 如果没有指定上面的 registry 配置,即为直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息 42 | 'nodes' => [ 43 | ['host' => '127.0.0.1', 'port' => 9504], 44 | ], 45 | // 配置项,会影响到 Packer 和 Transporter 46 | 'options' => [ 47 | 'connect_timeout' => 5.0, 48 | 'recv_timeout' => 5.0, 49 | 'settings' => [ 50 | 'open_length_check' => true, 51 | 'package_length_type' => 'N', 52 | 'package_length_offset' => 0, 53 | 'package_body_offset' => 4, 54 | ], 55 | // 当使用 JsonRpcPoolTransporter 时会用到以下配置 56 | 'pool' => [ 57 | 'min_connections' => 1, 58 | 'max_connections' => 50, 59 | 'connect_timeout' => 10.0, 60 | 'wait_timeout' => 3.0, 61 | 'heartbeat' => -1, 62 | 'max_idle_time' => 60.0, 63 | ], 64 | ], 65 | ]; 66 | } 67 | return $consumers; 68 | }), 69 | ]; 70 | -------------------------------------------------------------------------------- /config/autoload/view.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use Hyperf\View\Mode; 15 | use Hyperf\ViewEngine\HyperfViewEngine; 16 | 17 | return [ 18 | 'engine' => HyperfViewEngine::class, 19 | 'mode' => Mode::SYNC, 20 | 'config' => [ 21 | 'view_path' => BASE_PATH . '/storage/view/', 22 | 'cache_path' => BASE_PATH . '/runtime/view/', 23 | 'charset' => 'UTF-8', 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /config/autoload/websocket_client.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | return [ 15 | 'ws1' => [ 16 | 'host' => env('WEBSOCKET_HOST', 'localhost'), 17 | 'port' => (int) env('WEBSOCKET_PORT', 9502), 18 | 'ws' => 'ws://', 19 | 'auto_close' => false, 20 | 'pool' => [ 21 | 'min_connections' => 1, 22 | 'max_connections' => 10, 23 | 'connect_timeout' => 10.0, 24 | 'wait_timeout' => 3.0, 25 | 'heartbeat' => -1, 26 | 'max_idle_time' => (float) env('WEBSOCKET_MAX_IDLE_TIME', 60), 27 | ], 28 | ], 29 | 'ws2' => [ 30 | 'host' => env('WEBSOCKET_HOST', 'localhost'), 31 | 'port' => (int) env('WEBSOCKET_PORT', 9502), 32 | 'ws' => 'ws://', 33 | 'auto_close' => false, 34 | 'pool' => [ 35 | 'min_connections' => 1, 36 | 'max_connections' => 10, 37 | 'connect_timeout' => 10.0, 38 | 'wait_timeout' => 3.0, 39 | 'heartbeat' => -1, 40 | 'max_idle_time' => (float) env('WEBSOCKET_MAX_IDLE_TIME', 60), 41 | ], 42 | ], 43 | ]; 44 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | 13 | * @link https://github.com/Hyperf-Glory/socket-io 14 | */ 15 | 16 | use Hyperf\Contract\StdoutLoggerInterface; 17 | use Psr\Log\LogLevel; 18 | 19 | return [ 20 | 'app_name' => env('APP_NAME', 'skeleton'), 21 | 'app_env' => env('APP_ENV', 'dev'), 22 | 'image_url' => env('IMAGE_URL', 'http://127.0.0.1:9500'), 23 | 'scan_cacheable' => env('SCAN_CACHEABLE', true), 24 | StdoutLoggerInterface::class => [ 25 | 'log_level' => [ 26 | LogLevel::ALERT, 27 | LogLevel::CRITICAL, 28 | LogLevel::DEBUG, 29 | LogLevel::EMERGENCY, 30 | LogLevel::ERROR, 31 | LogLevel::INFO, 32 | LogLevel::NOTICE, 33 | LogLevel::WARNING, 34 | ], 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /config/container.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | use Hyperf\Di\Container; 15 | use Hyperf\Di\Definition\DefinitionSourceFactory; 16 | use Hyperf\Utils\ApplicationContext; 17 | use Psr\Container\ContainerInterface; 18 | 19 | $container = new Container((new DefinitionSourceFactory(true))()); 20 | 21 | if (! $container instanceof ContainerInterface) { 22 | throw new RuntimeException('The dependency injection container is invalid.'); 23 | } 24 | return ApplicationContext::setContainer($container); 25 | -------------------------------------------------------------------------------- /deploy.test.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | hyperf: 4 | image: $REGISTRY_URL/$PROJECT_NAME:test 5 | environment: 6 | - "APP_PROJECT=hyperf" 7 | - "APP_ENV=test" 8 | ports: 9 | - 9501:9501 10 | deploy: 11 | replicas: 1 12 | restart_policy: 13 | condition: on-failure 14 | delay: 5s 15 | max_attempts: 5 16 | update_config: 17 | parallelism: 2 18 | delay: 5s 19 | order: start-first 20 | networks: 21 | - hyperf_net 22 | configs: 23 | - source: hyperf_v1.0 24 | target: /opt/www/.env 25 | configs: 26 | hyperf_v1.0: 27 | external: true 28 | networks: 29 | hyperf_net: 30 | external: true 31 | -------------------------------------------------------------------------------- /hyperf-chat 架构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hyperf-Glory/socket-io/09e260edba8de7681bfad50d605501ca1fd83ac5/hyperf-chat 架构.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | # Magic behaviour with __get, __set, __call and __callStatic is not exactly static analyser-friendly :) 2 | # Fortunately, You can ingore it by the following config. 3 | # 4 | # vendor/bin/phpstan analyse app --memory-limit 200M -l 0 5 | # 6 | parameters: 7 | reportUnmatchedIgnoredErrors: false 8 | ignoreErrors: 9 | - '#Static call to instance method Hyperf\\HttpServer\\Router\\Router::[a-zA-Z0-9\\_]+\(\)#' 10 | - '#Static call to instance method Hyperf\\DbConnection\\Db::[a-zA-Z0-9\\_]+\(\)#' 11 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./test 14 | 15 | 16 | 17 | 18 | ./app 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /public/files/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hyperf-Glory/socket-io/09e260edba8de7681bfad50d605501ca1fd83ac5/public/files/.gitignore -------------------------------------------------------------------------------- /public/media/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hyperf-Glory/socket-io/09e260edba8de7681bfad50d605501ca1fd83ac5/public/media/.gitignore -------------------------------------------------------------------------------- /public/tmp/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hyperf-Glory/socket-io/09e260edba8de7681bfad50d605501ca1fd83ac5/public/tmp/.gitignore -------------------------------------------------------------------------------- /storage/view/emails/verify-code.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | 18 | 49 | 50 | 51 |
19 |
20 | 21 | 22 |
23 |
24 |
25 |
26 | 尊敬的用户:您好! 27 | 28 | 您正在进行 {{$service_name}} 操作,请在验证码输入框中输入:{{$sms_code}},以完成操作,验证码有效期15分钟。 29 | 30 |
31 |
32 | 33 |

34 | 注意:此操作可能会修改您的密码、修改邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证帐户安全 35 |
(工作人员不会向你索取此验证码,请勿泄漏!) 36 |

37 |
38 |
39 |
40 |
41 |
42 |

此为系统邮件,请勿回复
43 | 请保管好您的邮箱,避免账号被他人盗用 44 |

45 |

Hyperf Chat在线聊天

46 |
47 |
48 |
52 | 53 | -------------------------------------------------------------------------------- /test/Cases/ExampleTest.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace HyperfTest\Cases; 15 | 16 | use HyperfTest\HttpTestCase; 17 | 18 | /** 19 | * @internal 20 | * @coversNothing 21 | */ 22 | class ExampleTest extends HttpTestCase 23 | { 24 | public function testExample() 25 | { 26 | $this->assertTrue(true); 27 | $this->assertTrue(is_array($this->get('/'))); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Http.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | Swoole\Coroutine\run(function () { 15 | for ($i = 0; $i < 1000000; ++$i) { 16 | \Swoole\Coroutine::create(function () { 17 | Swoole\Coroutine::sleep(1); 18 | }); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /test/HttpTestCase.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | namespace HyperfTest; 15 | 16 | use Hyperf\Testing\Client; 17 | use PHPUnit\Framework\TestCase; 18 | 19 | /** 20 | * Class HttpTestCase. 21 | * @method get($uri, $data = [], $headers = []) 22 | * @method post($uri, $data = [], $headers = []) 23 | * @method json($uri, $data = [], $headers = []) 24 | * @method file($uri, $data = [], $headers = []) 25 | * @method request($method, $path, $options = []) 26 | */ 27 | abstract class HttpTestCase extends TestCase 28 | { 29 | /** 30 | * @var Client 31 | */ 32 | protected $client; 33 | 34 | public function __construct($name = null, array $data = [], $dataName = '') 35 | { 36 | parent::__construct($name, $data, $dataName); 37 | $this->client = make(Client::class); 38 | } 39 | 40 | public function __call($name, $arguments) 41 | { 42 | return $this->client->{$name}(...$arguments); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://github.com/Hyperf-Glory/socket-io 13 | */ 14 | ini_set('display_errors', 'on'); 15 | ini_set('display_startup_errors', 'on'); 16 | 17 | error_reporting(E_ALL); 18 | date_default_timezone_set('Asia/Shanghai'); 19 | 20 | ! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1)); 21 | ! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL); 22 | 23 | Swoole\Runtime::enableCoroutine(true); 24 | 25 | require BASE_PATH . '/vendor/autoload.php'; 26 | 27 | Hyperf\Di\ClassLoader::init(); 28 | 29 | $container = require BASE_PATH . '/config/container.php'; 30 | 31 | $container->get(Hyperf\Contract\ApplicationInterface::class); 32 | --------------------------------------------------------------------------------