master
elf 2 years ago
commit 4bc96e314e

@ -0,0 +1,17 @@
APP_NAME=skeleton
APP_ENV=dev
DB_DRIVER=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=hyperf
DB_USERNAME=root
DB_PASSWORD=
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
DB_PREFIX=
REDIS_HOST=localhost
REDIS_AUTH=(null)
REDIS_PORT=6379
REDIS_DB=0

@ -0,0 +1,54 @@
# Default Dockerfile
#
# @link https://www.hyperf.io
# @document https://hyperf.wiki
# @contact group@hyperf.io
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
FROM hyperf/hyperf:8.0-alpine-v3.12-swoole
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
APP_ENV=prod \
SCAN_CACHEABLE=(true)
# update
RUN set -ex \
# show php version and extensions
&& php -v \
&& php -m \
&& php --ri swoole \
# ---------- some config ----------
&& cd /etc/php8 \
# - config PHP
&& { \
echo "upload_max_filesize=128M"; \
echo "post_max_size=128M"; \
echo "memory_limit=1G"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99_overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- clear works ----------
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
WORKDIR /opt/www
# Composer Cache
# COPY ./composer.* /opt/www/
# RUN composer install --no-dev --no-scripts
COPY . /opt/www
RUN print "\n" | composer install -o && php bin/hyperf.php
EXPOSE 9501
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]

@ -0,0 +1,12 @@
name: Build Docker
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Build
run: cp -rf .github/workflows/Dockerfile . && docker build -t hyperf .

@ -0,0 +1,25 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Release
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false

13
.gitignore vendored

@ -0,0 +1,13 @@
.buildpath
.settings/
.project
*.patch
.idea/
.git/
runtime/
vendor/
.phpintel/
.env
.DS_Store
.phpunit*
*.cache

@ -0,0 +1,57 @@
# usermod -aG docker gitlab-runner
stages:
- build
- deploy
variables:
PROJECT_NAME: hyperf
REGISTRY_URL: registry-docker.org
build_test_docker:
stage: build
before_script:
# - git submodule sync --recursive
# - git submodule update --init --recursive
script:
- docker build . -t $PROJECT_NAME
- docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:test
- docker push $REGISTRY_URL/$PROJECT_NAME:test
only:
- test
tags:
- builder
deploy_test_docker:
stage: deploy
script:
- docker stack deploy -c deploy.test.yml --with-registry-auth $PROJECT_NAME
only:
- test
tags:
- test
build_docker:
stage: build
before_script:
# - git submodule sync --recursive
# - git submodule update --init --recursive
script:
- docker build . -t $PROJECT_NAME
- docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME
- docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:latest
- docker push $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME
- docker push $REGISTRY_URL/$PROJECT_NAME:latest
only:
- tags
tags:
- builder
deploy_docker:
stage: deploy
script:
- echo SUCCESS
only:
- tags
tags:
- builder

@ -0,0 +1,91 @@
<?php
$header = <<<'EOF'
This file is part of Hyperf.
@link https://www.hyperf.io
@document https://hyperf.wiki
@contact group@hyperf.io
@license https://github.com/hyperf/hyperf/blob/master/LICENSE
EOF;
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@PSR2' => true,
'@Symfony' => true,
'@DoctrineAnnotation' => true,
'@PhpCsFixer' => true,
'header_comment' => [
'comment_type' => 'PHPDoc',
'header' => $header,
'separate' => 'none',
'location' => 'after_declare_strict',
],
'array_syntax' => [
'syntax' => 'short'
],
'list_syntax' => [
'syntax' => 'short'
],
'concat_space' => [
'spacing' => 'one'
],
'blank_line_before_statement' => [
'statements' => [
'declare',
],
],
'general_phpdoc_annotation_remove' => [
'annotations' => [
'author'
],
],
'ordered_imports' => [
'imports_order' => [
'class', 'function', 'const',
],
'sort_algorithm' => 'alpha',
],
'single_line_comment_style' => [
'comment_types' => [
],
],
'yoda_style' => [
'always_move_variable' => false,
'equal' => false,
'identical' => false,
],
'phpdoc_align' => [
'align' => 'left',
],
'multiline_whitespace_before_semicolons' => [
'strategy' => 'no_multi_line',
],
'constant_case' => [
'case' => 'lower',
],
'class_attributes_separation' => true,
'combine_consecutive_unsets' => true,
'declare_strict_types' => true,
'linebreak_after_opening_tag' => true,
'lowercase_static_reference' => true,
'no_useless_else' => true,
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'not_operator_with_space' => false,
'ordered_class_elements' => true,
'php_unit_strict' => false,
'phpdoc_separation' => false,
'single_quote' => true,
'standardize_not_equals' => true,
'multiline_comment_opening_closing' => true,
])
->setFinder(
PhpCsFixer\Finder::create()
->exclude('public')
->exclude('runtime')
->exclude('vendor')
->in(__DIR__)
)
->setUsingCache(false);

@ -0,0 +1,11 @@
<?php
namespace PHPSTORM_META {
// Reflect
override(\Psr\Container\ContainerInterface::get(0), map('@'));
override(\Hyperf\Utils\Context::get(0), map('@'));
override(\make(0), map('@'));
override(\di(0), map('@'));
}

@ -0,0 +1,54 @@
# Default Dockerfile
#
# @link https://www.hyperf.io
# @document https://hyperf.wiki
# @contact group@hyperf.io
# @license https://github.com/hyperf/hyperf/blob/master/LICENSE
FROM hyperf/hyperf:8.0-alpine-v3.12-swoole
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
APP_ENV=prod \
SCAN_CACHEABLE=(true)
# update
RUN set -ex \
# show php version and extensions
&& php -v \
&& php -m \
&& php --ri swoole \
# ---------- some config ----------
&& cd /etc/php8 \
# - config PHP
&& { \
echo "upload_max_filesize=128M"; \
echo "post_max_size=128M"; \
echo "memory_limit=1G"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99_overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- clear works ----------
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
WORKDIR /opt/www
# Composer Cache
# COPY ./composer.* /opt/www/
# RUN composer install --no-dev --no-scripts
COPY . /opt/www
RUN composer install --no-dev -o && php bin/hyperf.php
EXPOSE 9501
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]

@ -0,0 +1,36 @@
# Introduction
This is a skeleton application using the Hyperf framework. This application is meant to be used as a starting place for those looking to get their feet wet with Hyperf Framework.
# Requirements
Hyperf has some requirements for the system environment, it can only run under Linux and Mac environment, but due to the development of Docker virtualization technology, Docker for Windows can also be used as the running environment under Windows.
The various versions of Dockerfile have been prepared for you in the [hyperf/hyperf-docker](https://github.com/hyperf/hyperf-docker) project, or directly based on the already built [hyperf/hyperf](https://hub.docker.com/r/hyperf/hyperf) Image to run.
When you don't want to use Docker as the basis for your running environment, you need to make sure that your operating environment meets the following requirements:
- PHP >= 7.3
- Swoole PHP extension >= 4.5and Disabled `Short Name`
- OpenSSL PHP extension
- JSON PHP extension
- PDO PHP extension If you need to use MySQL Client
- Redis PHP extension If you need to use Redis Client
- Protobuf PHP extension If you need to use gRPC Server of Client
# Installation using Composer
The easiest way to create a new Hyperf project is to use Composer. If you don't have it already installed, then please install as per the documentation.
To create your new Hyperf project:
$ composer create-project hyperf/hyperf-skeleton path/to/install
Once installed, you can run the server immediately using the command below.
$ cd path/to/install
$ php bin/hyperf.php start
This will start the cli-server on port `9501`, and bind it to all network interfaces. You can then visit the site at `http://localhost:9501/`
which will bring up Hyperf default home page.

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Helper\Client\Lanhuo;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Command\Annotation\Command;
use Hyperf\Contract\ContainerInterface;
/**
* @Command
*/
class JinlingCommand extends HyperfCommand
{
/**
* @var ContainerInterface
*/
protected $container;
protected $admin;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
parent::__construct('jinling');
}
public function configure()
{
parent::configure();
$this->setDescription('廖金灵测试');
}
public function handle()
{
Lanhuo::addUser([
'user_id' => 111,
'account' => 'bbbbbbbabc111',
'phone' => '18888888888',
'device_type' => 2, // 设备类型(1-andriod 2-ios)
'device_number' => 'ABCD',
'name' =>'哈哈哈',
'id_card' => '350824199911111111',
'is_real' => 1, // 是否实名(1-实名 0-未实名)
]);
}
}

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Constants;
use Hyperf\Constants\AbstractConstants;
use Hyperf\Constants\Annotation\Constants;
/**
* @Constants
*/
class ResultCode extends AbstractConstants
{
/**
* @Message("成功")
*/
const SUCCESS = 1000;
/**
* @Message("错误")
*/
const DEFAULT_ERROR = 2000;
/**
* @Message("Server Error")
*/
const SERVER_ERROR = 2001;
/**
* @Message("参数错误")
*/
const SYSTEM_INVALID = 2002;
/**
* @Message("Params %s is invalid.")
*/
const PARAMS_INVALID = 2003;
/**
* @Message("记录不存在")
*/
const RECORD_NOT_FOUND = 2004;
}

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Controller;
use App\Constants\ResultCode;
use App\Helper\Result;
use Hyperf\Context\Context;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerInterface;
abstract class AbstractController
{
/**
* @Inject
* @var ContainerInterface
*/
protected $container;
/**
* @Inject
* @var RequestInterface
*/
protected $request;
/**
* @Inject
* @var ResponseInterface
*/
protected $response;
protected function success(string $message = '', int $code = ResultCode::SUCCESS): Result
{
return $this->respond($code, $message);
}
protected function respond(int $code, string $message): Result
{
$data = Context::get('respondData', []);
return new Result($code, $message, $data);
}
protected function setData(array $data): AbstractController
{
Context::set('respondData', $data);
return $this;
}
}

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Exception\BusinessException;
use App\Job\AfterLoginJob;
use App\Job\AfterRegisterJob;
use App\Job\AfterSaveRoleJob;
use App\Job\AfterSpendJob;
use App\Helper\Queue;
class GameEventController extends AbstractController
{
public function __construct()
{
}
public function recharge()
{
$payOrderNumber = $this->request->input('pay_order_number', '');
if (!$payOrderNumber) {
throw new BusinessException('缺少参数[pay_order_number]');
}
Queue::push(AfterSpendJob::class, ['pay_order_number' => $payOrderNumber]);
return $this->success();
}
public function login()
{
$userId = (int)$this->request->input('user_id', 0);
$gameId = (int)$this->request->input('game_id', 0);
$loginTime = (int)$this->request->input('login_time', 0);
if (!$userId) {
throw new BusinessException('缺少参数[user_id]');
}
if (!$gameId) {
throw new BusinessException('缺少参数[game_id]');
}
if (!$loginTime) {
throw new BusinessException('缺少参数[login_time]');
}
Queue::push(AfterLoginJob::class, [
'user_id' => $userId,
'game_id' => $gameId,
'login_time' => $loginTime
]);
return $this->success();
}
public function register()
{
$userId = $this->request->input('user_id', 0);
$source = $this->request->input('source', 'SDK');
if (!$userId) {
throw new BusinessException('缺少参数[user_id]');
}
Queue::push(AfterRegisterJob::class, [
'user_id' => $userId,
]);
return $this->success();
}
public function saveRole()
{
Queue::push(AfterSaveRoleJob::class, $this->request->all());
return $this->success();
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Controller;
class IndexController extends AbstractController
{
public function index()
{
$user = $this->request->input('user', 'Hyperf');
$method = $this->request->getMethod();
return [
'method' => $method,
'message' => "Hello {$user}.",
];
}
}

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Service\PlayerService;
class StatisticsController extends AbstractController
{
/**
* @var PlayerService $playerService
*/
protected $playerService;
public function __construct(PlayerService $playerService)
{
$this->playerService = $playerService;
}
public function playerRetention()
{
$records = $this->playerService->getPlayerRetention($this->request->all());
return $this->setData(['records' => $records])->success();
}
}

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Exception;
use App\Constants\ResultCode;
use Hyperf\Server\Exception\ServerException;
use Throwable;
class BusinessException extends ServerException
{
public function __construct(string $message = null, int $code = ResultCode::DEFAULT_ERROR, Throwable $previous = null)
{
if (is_null($message)) {
$message = ResultCode::getMessage($code);
}
parent::__construct($message, $code, $previous);
}
}

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace App\Exception\Handler;
use App\Constants\ResultCode;
use App\Exception\BusinessException;
use App\Helper\Result;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use Hyperf\Database\Model\ModelNotFoundException;
class AppExceptionHandler extends ExceptionHandler
{
/**
* @var StdoutLoggerInterface
*/
protected $logger;
public function __construct(StdoutLoggerInterface $logger)
{
$this->logger = $logger;
}
public function handle(Throwable $throwable, ResponseInterface $response)
{
$data = [];
$code = ResultCode::SERVER_ERROR;
$message = '';
$httpCode = 200;
if ($throwable instanceof BusinessException) {
$this->stopPropagation();
$code = $throwable->getCode();
$message = $throwable->getMessage();
} elseif ($throwable instanceof ModelNotFoundException) {
$this->stopPropagation();
$code = ResultCode::RECORD_NOT_FOUND;
$message = ResultCode::getMessage($code);
} else {
if (config('app_env') == 'dev') {
$message = $throwable->getMessage();
$data = [
'line' => $throwable->getLine(),
'file' => $throwable->getFile(),
];
} else {
$message = ResultCode::getMessage($code);
}
$this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
$this->logger->error($throwable->getTraceAsString());
}
$result = new Result($code, $message, $data);
return $response
->withAddedHeader('content-type', 'application/json; charset=utf-8')
->withStatus($httpCode)
->withBody(new SwooleStream(strval($result)));
}
public function isValid(Throwable $throwable): bool
{
return true;
}
}

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Hyperf\Di\Annotation\AnnotationCollector;
class Annotation
{
/**
* 只支持系统默认收集器
*/
public static function getMethodsByAnnotationAndClass(string $annotation, string $class): array
{
$items = AnnotationCollector::getMethodsByAnnotation($annotation);
$methods = [];
foreach ($items as $item) {
if ($item['class'] == $class) {
$methods[] = $item;
}
}
return $methods;
}
/**
* 只支持系统默认收集器
*/
public static function getPropertiesByAnnotationAndClass(string $annotation, string $class): array
{
$items = AnnotationCollector::getPropertiesByAnnotation($annotation);
$properties = [];
foreach ($items as $item) {
if ($item['class'] == $class) {
$properties[] = $item;
}
}
return $properties;
}
}

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Exception\BusinessException;
use Hyperf\Utils\ApplicationContext;
/**
* 缓存辅助类
* @method static mixed get(string $key, mixed $default = null)
* @method static bool set(string $key, mixed $value, null|int|\DateInterval $ttl = null)
* @method static bool delete(string $key)
* @method static bool clear()
* @method static bool deleteMultiple(iterable $keys)
* @method static iterable getMultiple(iterable $keys, mixed $default = null)
* @method static bool setMultiple(iterable $values, null|int|\DateInterval $ttl = null)
* @method static bool has(string $key)
*/
class Cache
{
private static $instance;
private static $cacheKeys = [
];
public static function getKey($name, array $params = []): string
{
$cacheKey = self::$cacheKeys[$name] ?? null;
if (is_null($cacheKey)) {
throw new BusinessException('缓存不存在');
}
foreach ($params as $key => $value) {
$cacheKey = str_replace('{' . $key . '}', $value, $cacheKey);
}
return $cacheKey;
}
public static function getInstance()
{
if (is_null(self::$instance)) {
self::$instance = ApplicationContext::getContainer()->get(\Psr\SimpleCache\CacheInterface::class);
}
return self::$instance;
}
public static function __callStatic($method, $args)
{
return call_user_func_array([self::getInstance(), $method], $args);
}
}

@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace App\Helper\Client;
use App\Exception\BusinessException;
use App\Helper\Log;
use GuzzleHttp\Client;
use Hyperf\Guzzle\CoroutineHandler;
use GuzzleHttp\HandlerStack;
class Lanhuo
{
const SUCCESS = 0;
private static $accessToken;
private static $expiredAt;
private static $client;
public static function addUser($data)
{
$params = [
'user_id' => $data['user_id'],
'account' => $data['account'],
'phone' => $data['phone'],
'device_type' => $data['device_type'], // 设备类型(1-andriod 2-ios)
'device_number' => $data['device_number'],
'name' => $data['name'],
'id_card' => $data['id_card'],
'is_real' => $data['is_real'], // 是否实名(1-实名 0-未实名)
];
return self::api('/llwf/user/add', $params);
}
public static function updateUser($data)
{
$params = [
'user_id' => $data['user_id'],
'phone' => $data['phone'],
'device_type' => $data['device_type'], // 设备类型(1-andriod 2-ios)
'device_number' => $data['device_number'],
'name' => $data['name'],
'id_card' => $data['id_card'],
'is_real' => $data['is_real'], // 是否实名(1-实名 0-未实名)
];
return self::api('/llwf/user/update', $params);
}
public static function addLoginLog($data)
{
$params = [
'user_id' => $data['user_id'],
'account' => $data['account'],
'phone' => $data['phone'],
'device_type' => $data['device_type'], // 设备类型(1-andriod 2-ios)
'device_number' => $data['device_number'],
];
return self::api('/llwf/user/AddLoginLog', $params);
}
public static function addOrder($data)
{
$params = [
'order_code' => $data['pay_order_number'],
'user_id' => $data['user_id'],
'account' => $data['account'],
'service_id' => $data['service_id'],
'service_name' => $data['service_name'],
'role_id' => $data['role_id'],
'role_name' => $data['role_name'],
'game_name' => $data['game_name'],
'order_amount' => $data['order_amount'],
'pay_time' => $data['pay_time'],
'status' => $data['status'], //支付状态(待支付 0 已支付 1 支付失败 2)
];
return self::api('/llwf/order/add', $params);
}
public static function addRole($data)
{
$params = [
'user_id' => $data['user_id'],
'service_id' => $data['service_id'],
'service_name' => $data['service_name'],
'role_id' => $data['role_id'],
'role_name' => $data['role_name'],
'role_level' => $data['role_level'],
'profession_id' => '', // 职业ID
'profession_name' => '', // 职业名称
'power' => '', // 战力
];
return self::api('/llwf//role/add', $params);
}
private static function auth()
{
$params = [
'appId' => config('clients.lanhuo.app_id', ''),
'appKey' => config('clients.lanhuo.app_key', ''),
'appSecret' => config('clients.lanhuo.app_secret', ''),
];
return self::api('/auth', $params, false);
}
private static function api($uri, array $params, $withAccessToken = true)
{
$headers = [];
if ($withAccessToken) {
if (!self::$accessToken || time() > self::$expiredAt) {
self::refreshAccessToken();
}
$headers['accessToken'] = self::$accessToken;
}
$result = self::post($uri, $params, $headers);
if ($result['errorCode'] == self::SUCCESS) {
return $result['data'];
} else {
Log::error('LANHUO_REQUEST_ERROR[' . $uri . ']: ' . $result['msg'], $params, 'lanhuo');
throw new BusinessException('请求接口错误:' . $result['msg']);
}
}
private static function refreshAccessToken()
{
$data = self::auth();
self::$accessToken = $data['accessToken'];
self::$expiredAt = strtotime($data['createDt']) + $data['expireTime'] - 10; // 前置10秒
}
private static function post($uri, array $params, array $headers = []): array
{
try {
$response = self::getClient()->post('/openapi' . $uri, [
'verify' => false,
// 'form_params' => $params,
'json' => $params,
'headers' => $headers,
]);
$result = (string)$response->getBody();
return json_decode($result, true);
} catch (\Exception $e) {
Log::error('LANHUO_NET_ERROR[' . $uri . ']: ' . $e->getMessage(), $params, 'lanhuo');
return [
'errorCode' => 88888,
'msg' => env('APP_ENV') == 'prod' ? '网络异常' : '网络异常:' . $e->getMessage(),
'data' => [],
];
}
}
private static function getClient()
{
if (is_null(self::$client)) {
self::$client = new Client([
'base_uri' => config('clients.lanhuo.base_url', ''),
'handler' => HandlerStack::create(new CoroutineHandler()),
'timeout' => 5,
'swoole' => [
'timeout' => 10,
'socket_buffer_size' => 1024 * 1024 * 2,
],
'headers' => [
'Content-Type' => 'application/json',
],
]);
}
return self::$client;
}
}

@ -0,0 +1,212 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Hyperf\Database\Model\Collection;
use Hyperf\Database\Query\Builder;
use Hyperf\DbConnection\Db as DB;
class DataList implements Logic
{
public $name;
protected $params;
protected $page;
protected $limit;
protected $offset;
protected $pagination;
protected $records = [];
protected $isGroupQuery = false;
protected $withoutPaginate = false;
protected $isQuickPaginate = false;
protected $isSpanRow = false;
/**
* @param array $params 查询数据
*/
public function __construct(array $params = [])
{
$this->params = $params;
$this->check();
$this->init();
}
protected function init()
{
}
protected function beforeGenerate($items)
{
return $items;
}
protected function afterGenerate()
{
}
public function generateRecords($items)
{
$items = $this->beforeGenerate($items);
$this->records = [];
if ($this->isSpanRow) {
foreach ($items as $item) {
$this->records = array_merge($this->records, $this->toArray($item));
}
} else {
foreach ($items as $item) {
$this->records[] = $this->toArray($item);
}
}
$this->afterGenerate();
}
public function toArray($item)
{
return null;
}
public function query()
{
return null;
}
protected function statOffset($page, $limit)
{
return ($page - 1) * $limit;
}
public function paginate(): DataList
{
[$records, $pagination] = $this->doPaginate();
$this->pagination = $pagination;
$this->generateRecords($records);
return $this;
}
public function doPaginate(): array
{
$count = 0;
$this->page = $page = intval($this->params['page'] ?? 1);
$this->limit = $limit = intval($this->params['limit'] ?? 10);
$this->offset = $offset = $this->statOffset($page, $limit);
$records = new Collection([]);
$query = $this->query();
if (is_null($query)) {
$pagination = $this->generatePagination($page, $limit, $this->getCount());
return [$records, $pagination];
}
if ($this->withoutPaginate) {
$searchQuery = clone $query;
return [$searchQuery->get(), null];
}
// 临时快速分页
if ($this->isQuickPaginate) {
return $this->quickPaginate(clone $query, $page, $limit);
} else {
$count = $this->getCount();
if ($count) {
$searchQuery = clone $query;
$records = $searchQuery->offset($offset)->limit($limit)->get();
}
$pagination = $this->generatePagination($page, $limit, $count);
}
return [$records, $pagination];
}
protected function quickPaginate($query, $page, $limit)
{
$count = 1;
$offset = $this->statOffset($page, $limit);
$records = $query->offset($offset)->limit($limit+1)->get();
$recordCount = $records->count();
if ($recordCount < $limit+1) {
$hasMroe = false;
} else {
$records->pop();
$hasMroe = true;
}
$pagination = $this->generatePagination($page, $limit, $count, $hasMroe);
return [$records, $pagination];
}
protected function generatePagination($page, $limit, $count, $hasMroe = false)
{
$pageCount = intval(ceil($count/$limit));
if (! $hasMroe) {
if ($page < $pageCount) {
$hasMroe = true;
}
}
return [
'page' => $page,
'count' => $count,
'page_count' => $pageCount,
'limit' => $limit,
'has_more'=> $hasMroe,
];
}
public function getCount()
{
$query = $this->query();
if (is_null($query)) {
return 0;
}
$countQuery = clone $query;
if ($this->isGroupQuery) {
$countQuery->select([DB::raw('1')]);
return DB::table(DB::raw("({$countQuery->toSql()}) as temp_count_table"))
->mergeBindings($query instanceof Builder ? $countQuery : $countQuery->getQuery())
->count();
} else {
return $countQuery->count();
}
}
public function getPagination(): ?array
{
return $this->pagination;
}
public function setWithoutPaginate($withoutPaginate)
{
$this->withoutPaginate = $withoutPaginate;
return $this;
}
public function getRecords()
{
return $this->records;
}
public function getSummary()
{
return [];
}
public function check()
{
}
/**
* 获取 记录(records)/分页(pagination)/汇总(summary)
* @param array $appendOptions 追加信息一起返回
* @return array
*/
public function all($appendOptions = [])
{
$data = [
'records' => $this->getRecords(),
'pagination' => $this->getPagination(),
'summary' => $this->getSummary(),
];
return array_merge($data, $appendOptions);
}
}

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Helper;
class DeviceType
{
const ANDROID = 1;
const IOS = 2;
public static $deviceTypeList = [
self::ANDROID => '安卓',
self::IOS => '苹果',
];
public static function getDeviceTypeText($deviceType)
{
return self::$deviceTypeList[$deviceType] ?? '未知';
}
}

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Annotation\Dict as DictAnnotation;
use Hyperf\Di\Annotation\AnnotationCollector;
class Dict
{
/**
* @var int 是
*/
public const YES = 0;
/**
* @var int 否
*/
public const NO = 1;
public static function get(array $names, int $language)
{
return array_merge(self::getFromProperties($names), self::getFromMethods($names, $language));
}
private static function getFromProperties(array $names): array
{
$items = AnnotationCollector::getPropertiesByAnnotation(DictAnnotation::class);
$properties = [];
foreach ($items as $item) {
$properties[$item['annotation']->name] = ['class' => $item['class'], 'property' => $item['property'], 'transform' => $item['annotation']->transform];
}
$records = [];
foreach ($names as $name) {
if (isset($properties[$name])) {
$property = $properties[$name];
$items = $property['class']::${$property['property']};
if ($property['transform']) {
foreach ($items as $key => $value) {
$records[$name][] = ['key' => $key, 'value' => $value];
}
} else {
$records[$name] = $items;
}
}
}
return $records;
}
private static function getFromMethods(array $names, int $language): array
{
$items = AnnotationCollector::getMethodsByAnnotation(DictAnnotation::class);
$methods = [];
foreach ($items as $item) {
$methods[$item['annotation']->name] = ['class' => $item['class'], 'method' => $item['method']];
}
$records = [];
foreach ($names as $name) {
if (isset($methods[$name])) {
$method = $methods[$name];
$object = make($method['class']);
if ($language > 0) {
$records[$name] = $object->{$method['method']}($language);
} else {
$records[$name] = $object->{$method['method']}();
}
}
}
return $records;
}
}

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace App\Helper;
class Excel
{
public static function getColumnKey($index)
{
if ($index >= 256) {
throw new \Exception('Too much Excel column!');
}
$keys = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
if ($index < 26) {
return $keys[$index];
} else {
$first = $index/26;
$second = $index%26;
return $keys[$first-1] . $keys[$second];
}
}
public static function generateUniqueCode(array $params, string $identity, $length = 8)
{
$md5Code = md5(microtime(true) . json_encode($params) . $identity . rand(0, 9999));
return substr($md5Code, 0, $length);
}
}

@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Closure;
use Hyperf\Database\Model\Collection;
class Export
{
const MODE_QUERY = 1;
const MODE_ARRAY = 2;
const MODE_COLLECTION = 3;
public $name;
protected $params;
protected $basePath = BASE_PATH . '/public';
protected $writerType = '.xlsx';
protected $fileName;
protected $filePath;
protected $fileUrl;
protected $diskOptions;
protected $list;
public $chunkCompareEnable = true;
public $withSummary = false;
public $chunkCompareOptions = [
'key' => 'id',
'symbol' => '<'
];
public function __construct(array $params = [])
{
$this->params = $params;
$this->init();
}
protected function init()
{
}
public function query()
{
return null;
}
public function array(): array
{
return [];
}
public function collection(): Collection
{
return new Collection([]);
}
/**
* 导出时每行的数据
*/
public function map($item): array
{
return [];
}
/**
* 导出时的表头
*/
public function headings(): array
{
return [];
}
public function summary(): array
{
return [];
}
public function rangeQueryItems($items)
{
return $items;
}
/**
* 导出时文件后缀
*/
public function getExtension()
{
/* $extensions = [
Excel::XLSX => '.xlsx',
Excel::CSV => '.csv',
// Excel::TSV => '.tsv',
Excel::ODS => '.ods',
Excel::XLS => '.xls',
Excel::HTML => '.html',
Excel::MPDF => '.pdf',
Excel::DOMPDF => '.pdf',
Excel::TCPDF => '.pdf',
];
return $extensions[$this->writerType] ?? '.xlsx'; */
return '.xlsx';
}
public function generateFileName()
{
$uniqueCode = Excel::generateUniqueCode($this->params, '', 8);
if (!$this->name) {
$this->fileName = date('YmdHis') . $uniqueCode;
} else {
$this->fileName = date('YmdHis') . $uniqueCode . '_' . $this->name;
}
}
protected function generateFilePath()
{
if (!$this->fileName) {
$this->generateFileName();
}
$excelPath = getRoutePrefix() . '/excels/';
if (!is_dir($this->basePath . $excelPath)) {
mkdir($this->basePath . $excelPath);
}
$this->fileUrl = $excelPath . $this->fileName. $this->getExtension();
$this->filePath = $this->basePath . $this->fileUrl;
}
public function getMode()
{
return self::MODE_QUERY;
}
protected function prepare()
{
}
public function store()
{
$this->prepare();
$this->generateFilePath();
$exporter = new Exporter();
$exporter->store($this, $this->filePath);
}
/**
* 返回设置单元格样式的函数 function(Export, Worksheet)
*/
public function getStyleSetting(): ?Closure
{
return null;
}
public function setFileName($fileName)
{
return $this->fileName = $fileName;
}
public function getFileName()
{
return $this->fileName;
}
public function getFileUrl()
{
return $this->fileUrl;
}
protected function check()
{
}
public static function identity()
{
return Str::encrypt(static::class);
}
public static function getClassByIdentity($identity)
{
return Str::decrypt($identity);
}
}

@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Exception\BusinessException;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Exporter
{
private $styleSettingClosure;
/**
* @param object $export
* @param string|null $fileName
* @param string $writerType
* @param array $headers
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @return BinaryFileResponse
*/
public function download($export, string $fileName, string $writerType = null)
{
}
/**
* @param object $export
* @param string $filePath
* @param string|null $disk
* @param string $writerType
* @param mixed $diskOptions
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @return bool
*/
public function store(Export $export, string $filePath, string $writerType = null)
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$this->fillHeading($export, $sheet);
$this->fillData($export, $sheet);
$this->setAutoSize($export, $sheet);
$closure = $export->getStyleSetting();
if ($closure) {
$closure($export, $sheet);
}
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
}
private function fillHeading(Export $export, Worksheet $sheet)
{
$rowIndex = 0;
$firstCellKey = null;
$lastCellKey = null;
foreach ($export->headings() as $columnIndex => $name) {
$cellKey = $this->getCellKey($rowIndex, $columnIndex);
$sheet->setCellValue($cellKey, $name);
if ($columnIndex == 0) {
$firstCellKey = $cellKey;
}
$lastCellKey = $cellKey;
}
$this->setHeadingStyle($sheet, $firstCellKey . ':' . $lastCellKey);
}
private function fillData(Export $export, Worksheet $sheet, $rowIndex = 1)
{
$mode = $export->getMode();
if ($mode == Export::MODE_QUERY) {
$rowIndex = $this->fillFromQuery($export, $sheet, $rowIndex);
} elseif ($mode == Export::MODE_ARRAY) {
$rowIndex = $this->fillFromArray($export, $sheet, $rowIndex);
} elseif ($mode == Export::MODE_COLLECTION) {
$rowIndex = $this->fillFromCollection($export, $sheet, $rowIndex);
}
$summary = $export->withSummary ? $export->summary() : null;
if (!empty($summary)) {
$rowIndex = $this->fillItems($export, $sheet, [$summary], $rowIndex);
}
}
private function setHeadingStyle(Worksheet $sheet, $cellRange)
{
$sharedStyle = new Style();
$sharedStyle->applyFromArray(
[
'fill' => [
'fillType' => Fill::FILL_SOLID,
'color' => ['argb' => 'd9d9d9'],
],
'borders' => [
'allBorders' => ['borderStyle' => Border::BORDER_THIN],
],
'font' => [
'name' => '黑体',
'bold' => true,
'size' => 12
]
]
);
$sheet->duplicateStyle($sharedStyle, $cellRange);
}
private function fillFromQuery(Export $export, Worksheet $sheet, $rowIndex)
{
$query = $export->query();
if (is_null($query)) {
throw new BusinessException('query 方法未实现');
}
$page = 1;
$limit = 1000;
$chunkCompareValue = null;
$isFirstRound = true;
do {
$tmpQuery = clone $query;
$offset = ($page-1) * $limit;
$items = null;
if ($export->chunkCompareEnable) {
$options = $export->chunkCompareOptions;
if (!$isFirstRound) {
$tmpQuery->where($options['key'], $options['symbol'], $chunkCompareValue);
}
$items = $tmpQuery->limit($limit)->get();
$lastItem = $items->last();
$chunkCompareValue = $lastItem ? $lastItem->{$options['key']} : null;
} else {
$items = $tmpQuery->offset($offset)->limit($limit)->get();
}
$items = $export->rangeQueryItems($items);
$rowIndex = $this->fillItems($export, $sheet, $items, $rowIndex);
$isFirstRound = false;
$count = count($items);
$page ++;
} while ($count == $limit);
return $rowIndex;
}
private function fillFromArray(Export $export, Worksheet $sheet, $rowIndex)
{
return $this->fillItems($export, $sheet, $export->array(), $rowIndex);
}
private function fillFromCollection(Export $export, Worksheet $sheet, $rowIndex)
{
return $this->fillItems($export, $sheet, $export->collection(), $rowIndex);
}
private function fillItems(Export $export, Worksheet $sheet, $items, $rowIndex)
{
foreach ($items as $item) {
foreach ($export->map($item) as $columnIndex => $value) {
$cellKey = $this->getCellKey($rowIndex, $columnIndex);
$sheet->setCellValue($cellKey, $value);
}
$rowIndex += 1;
}
return $rowIndex;
}
private function concatCellKey($rowKey, $columnKey)
{
return $columnKey . $rowKey;
}
private function getCellKey($rowIndex, $columnIndex)
{
return $this->concatCellKey($rowIndex + 1, Excel::getColumnKey($columnIndex));
}
private function setAutoSize(Export $export, Worksheet $sheet)
{
foreach ($export->headings() as $columnIndex => $name) {
$columnKey = Excel::getColumnKey($columnIndex);
$sheet->getColumnDimension($columnKey)->setAutoSize(true);
}
}
}

@ -0,0 +1,211 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Annotation\Validation\After;
use App\Exception\BusinessException;
use App\Exception\ValidationException;
use Hyperf\Contract\ValidatorInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Contracts\MessageBag;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
class Form implements Logic
{
/**
* @Inject
* @var ValidatorFactoryInterface
*/
protected $validationFactory;
protected $_data = [];
protected $scene;
protected $isCheckScene;
protected $scenes = [];
/**
* @var ValidatorInterface $validator
*/
protected $validator;
/**
* @param array $data 表单数据
*/
public function __construct(array $data = [])
{
foreach ($this->defaults() as $key => $value) {
if (isset($data[$key])) {
if (empty($value) && empty($data[$key])) {
$data[$key] = $value;
}
} else {
$data[$key] = $value;
}
}
$this->_data = $data;
}
/**
* 设置表单参数默认值,暂不支持多维数组
* @return array
*/
public function defaults(): array
{
return [];
}
public function rules(): array
{
return [];
}
public function messages(): array
{
return [];
}
public function attributes(): array
{
return [];
}
protected function before()
{
}
protected final function handleBefore()
{
$this->before();
$this->checkScene();
}
protected function getRules()
{
$rules = $this->rules();
$scene = $this->getScene();
if ($scene && isset($this->scenes[$scene]) && is_array($this->scenes[$scene])) {
$newRules = [];
foreach ($this->scenes[$scene] as $field) {
if (array_key_exists($field, $rules)) {
$newRules[$field] = $rules[$field];
}
}
return $newRules;
}
return $rules;
}
public final function validate($throwException = true)
{
$this->handleBefore();
$this->validator = $this->validationFactory->make($this->_data, $this->getRules(), $this->messages(), $this->attributes());
$this->handleAfter();
$fails = $this->validator->fails();
if ($throwException && $fails) {
throw new BusinessException($this->getErrorMessage());
}
return !$fails;
}
protected function after()
{
}
protected final function handleAfter()
{
$this->validator->after(function ($validator) {
$items = Annotation::getMethodsByAnnotationAndClass(After::class, static::class);
foreach ($items as $item) {
if (empty($item['annotation']->scenes) || in_array($this->scene, $item['annotation']->scenes)) {
$this->{$item['method']}();
}
}
});
$this->after();
}
public function addError($field, $message)
{
$this->validator->errors()->add($field, $message);
}
public function errors(): MessageBag
{
return $this->validator->errors();
}
public function getErrorMessage($isAll = false): string
{
if ($this->errors()->count() > 0) {
$messages = $this->errors()->all();
return $isAll ? implode('', $messages) : $messages[0];
}
return '';
}
public final function getData()
{
return $this->_data;
}
public final function getSceneData()
{
$scene = $this->getScene();
if ($scene && isset($this->scenes[$scene]) && is_array($this->scenes[$scene])) {
$newData = [];
foreach ($this->scenes[$scene] as $field) {
if (array_key_exists($field, $this->_data)) {
$newData[$field] = $this->_data[$field];
}
}
return $newData;
}
return $this->_data;
}
/**
* @param string $scene 场景名称
* @param bool $isCheckScene 是否检查场景是否存在
*/
public final function setScene(string $scene, bool $isCheckScene = true): void
{
$this->scene = $scene;
$this->isCheckScene = $isCheckScene;
}
protected final function checkScene()
{
if ($this->getScene() && $this->isCheckScene && !isset($this->scenes[$this->scene])) {
throw new BusinessException('Scene undefined!');
}
}
public final function getScene()
{
return $this->scene;
}
public function __get($key)
{
return isset($this->_data[$key]) ? $this->_data[$key] : null;
}
public function __set($key, $value)
{
$this->_data[$key] = $value;
}
public function __isset($key)
{
return $this->__get($key);
}
}

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Router\Router;
if (!function_exists('loadRoutes')) {
/**
* 加载路由.
*/
function loadRoutes($path)
{
$files = scandir($path);
foreach ($files as $file) {
if ($file == '.' || $file == '..') {
continue;
}
$filePath = $path . '/' . $file;
if (is_dir($filePath)) {
$prefix = '/' . $file;
$options = [];
if (file_exists($filePath . '/_init.php')) {
$config = require($filePath . '/_init.php');
$prefix = $config['prefix'] ?? $prefix;
$options = $config['options'] ?? $options;
}
Router::addGroup($prefix, function () use ($filePath) {
loadRoutes($filePath);
}, $options);
} elseif (is_file($filePath)) {
if ($file == '_init.php') {
continue;
}
require_once $filePath;
}
}
}
}
if (!function_exists('getClientIp')) {
function getClientIp(RequestInterface $request)
{
$headers = $request->getHeaders();
if(isset($headers['x-forwarded-for'][0]) && !empty($headers['x-forwarded-for'][0])) {
$ips = explode(',', $headers['x-forwarded-for'][0]);
return trim($ips[0]);
} elseif (isset($headers['x-real-ip'][0]) && !empty($headers['x-real-ip'][0])) {
return $headers['x-real-ip'][0];
}
$serverParams = $request->getServerParams();
return $serverParams['remote_addr'] ?? '';
}
}

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Exception\BusinessException;
use Hyperf\Database\Model\Collection;
class ListExport extends Export implements Logic
{
protected $params;
/**
* @var DataList
*/
protected $list;
protected function init()
{
$this->generateList();
if (empty($this->name)) {
$this->name = $this->list->name;
}
$this->check();
}
public function query()
{
return $this->list->query();
}
public function array(): array
{
return $this->list->getRecords();
}
public function summary(): array
{
return $this->list->getSummary();
}
public function rangeQueryItems($items)
{
$this->list->generateRecords($items);
return $this->list->getRecords();
}
protected function prepare()
{
$query = $this->query();
if ($this->getMode() == self::MODE_QUERY) {
if (is_null($query)) {
throw new BusinessException('query不能为空');
}
} else {
$items = new Collection([]);
if (!is_null($query)) {
$items = $query->get();
}
$this->list->generateRecords($items);
}
}
protected function listClass()
{
return null;
}
protected function generateList()
{
$class = $this->listClass();
if ($class) {
$this->list = new $class($this->params);
} else {
throw new BusinessException('未配置对应DataList');
}
}
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\ApplicationContext;
class Log
{
public static function get(string $name = 'app')
{
return ApplicationContext::getContainer()->get(LoggerFactory::class)->get($name);
}
public static function __callStatic($method, $args)
{
$name = 'app';
if (isset($args[2])) {
$name = $args[2];
unset($args[2]);
}
return call_user_func_array([self::get($name), $method], $args);
}
}

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Helper;
interface Logic
{
}

@ -0,0 +1,224 @@
<?php
declare(strict_types=1);
namespace App\Helper\Macro;
use App\Helper\Str;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Relations;
class WhereHasIn
{
/**
* @var Builder
*/
protected $builder;
/**
* @var string
*/
protected $relation;
/**
* @var string
*/
protected $nextRelation;
/**
* @var \Closure
*/
protected $callback;
/**
* @var string
*/
protected $method = 'whereIn';
public function __construct(Builder $builder, $relation, $callback)
{
$this->builder = $builder;
$this->relation = $relation;
$this->callback = $callback;
}
/**
* @return Builder
*
* @throws \Exception
*/
public function execute()
{
if (! $this->relation) {
return $this->builder;
}
return $this->where(
$this->formatRelation()
);
}
/**
* @param Relations\Relation $relation
*
* @return Builder
*
* @throws \Exception
*/
protected function where($relation)
{
if ($relation instanceof Relations\MorphTo) {
throw new \Exception('Please use whereHasMorphIn() for MorphTo relationships.');
}
$relationQuery = $this->getRelationQuery($relation);
$method = $this->method;
if (
$relation instanceof Relations\MorphOne
|| $relation instanceof Relations\MorphMany
) {
return $this->builder->{$method}(
$relation->getQualifiedParentKeyName(),
$this->withRelationQueryCallback(
$relationQuery
->select($relation->getQualifiedForeignKeyName())
->whereColumn($relation->getQualifiedParentKeyName(), $relation->getQualifiedForeignKeyName())
->where($relation->getQualifiedMorphType(), $relation->getMorphClass())
)
);
}
if ($relation instanceof Relations\MorphToMany) {
return $this->builder->{$method}(
$relation->getQualifiedParentKeyName(),
$this->withRelationQueryCallback(
$relationQuery
->select($relation->getQualifiedForeignPivotKeyName())
->whereColumn($relation->getQualifiedParentKeyName(), $relation->getQualifiedForeignPivotKeyName())
->where($relation->getTable().'.'.$relation->getMorphType(), $relation->getMorphClass())
)
);
}
// BelongsTo
if ($relation instanceof Relations\BelongsTo) {
return $this->builder->{$method}(
$this->getRelationQualifiedForeignKeyName($relation),
$this->withRelationQueryCallback(
$relationQuery
->select($relation->getQualifiedOwnerKeyName())
->whereColumn($this->getRelationQualifiedForeignKeyName($relation), $relation->getQualifiedOwnerKeyName())
)
);
}
if (
$relation instanceof Relations\HasOne
|| $relation instanceof Relations\HasMany
) {
return $this->builder->{$method}(
$relation->getQualifiedParentKeyName(),
$this->withRelationQueryCallback(
$relationQuery
->select($relation->getQualifiedForeignKeyName())
->whereColumn($relation->getQualifiedParentKeyName(), $relation->getQualifiedForeignKeyName())
)
);
}
// BelongsToMany
if ($relation instanceof Relations\BelongsToMany) {
return $this->builder->{$method}(
$relation->getQualifiedParentKeyName(),
$this->withRelationQueryCallback(
$relationQuery
->select($relation->getQualifiedForeignPivotKeyName())
->whereColumn($relation->getQualifiedParentKeyName(), $relation->getQualifiedForeignPivotKeyName())
)
);
}
if (
$relation instanceof Relations\HasOneThrough
|| $relation instanceof Relations\HasManyThrough
) {
return $this->builder->{$method}(
$relation->getQualifiedLocalKeyName(),
$this->withRelationQueryCallback(
$relationQuery
->select($relation->getQualifiedFirstKeyName())
->whereColumn($relation->getQualifiedLocalKeyName(), $relation->getQualifiedFirstKeyName())
)
);
}
throw new \Exception(sprintf('%s does not support "whereHasIn".', get_class($relation)));
}
/**
* @param Relations\Relation $relation
*
* @return Builder
*/
protected function getRelationQuery($relation)
{
$q = $relation->getQuery();
if ($this->builder->getModel()->getConnectionName() !== $q->getModel()->getConnectionName()) {
$databaseName = $this->getRelationDatabaseName($q);
$table = $q->getModel()->getTable();
if (! Str::contains($table, ["`$databaseName`.", "{$databaseName}."])) {
$q->from("{$databaseName}.{$table}");
}
}
return $q;
}
protected function getRelationDatabaseName($q)
{
return config('database.connections.'.$q->getModel()->getConnectionName().'.database');
}
protected function getRelationQualifiedForeignKeyName($relation)
{
if (method_exists($relation, 'getQualifiedForeignKeyName')) {
return $relation->getQualifiedForeignKeyName();
}
return $relation->getQualifiedForeignKey();
}
/**
* @return Relations\Relation
*/
protected function formatRelation()
{
if (is_object($this->relation)) {
$relation = $this->relation;
} else {
$relationNames = explode('.', $this->relation);
$this->nextRelation = implode('.', array_slice($relationNames, 1));
$method = $relationNames[0];
$relation = Relations\Relation::noConstraints(function () use ($method) {
return $this->builder->getModel()->$method();
});
}
return $relation;
}
/**
* @param Builder $relation
*
* @return Builder
*/
protected function withRelationQueryCallback($relationQuery)
{
return call_user_func($this->callback, $this->nextRelation, $relationQuery);
}
}

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Helper\Macro;
use Hyperf\Database\Model\Relations\Relation;
class WhereHasMorphIn
{
public static function make()
{
return function ($relation, $types, $callback = null, $boolean = 'and') {
$relation = $this->getRelationWithoutConstraints($relation);
$types = (array) $types;
if ($types === ['*']) {
$types = $this->model->newModelQuery()->distinct()->pluck($relation->getMorphType())->all();
foreach ($types as &$type) {
$type = Relation::getMorphedModel($type) ?? $type;
}
}
return $this->where(function ($query) use ($relation, $callback, $types) {
foreach ($types as $type) {
$query->orWhere(function ($query) use ($relation, $callback, $type) {
$belongsTo = $this->getBelongsToRelation($relation, $type);
if ($callback) {
$callback = function ($query) use ($callback, $type) {
return $callback($query, $type);
};
}
$query->where($relation->getRelated()->getTable().'.'.$relation->getMorphType(), '=', (new $type)->getMorphClass())
->whereHasIn($belongsTo, $callback);
});
}
}, null, null, $boolean);
};
}
}

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace App\Helper\Macro;
class WhereHasNotIn extends WhereHasIn
{
/**
* @var string
*/
protected $method = 'whereNotIn';
}

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Exception\BusinessException;
use Hyperf\Database\Model\Relations\Relation;
class Morph
{
/**
* 只支持多态关联实体的关联键为id
*
* @param int $morphId
* @param string $morphType
* @return void
*/
public static function getMorph(int $morphId, string $morphType)
{
$class = self::getMorphClass($morphType);
if (is_null($class)) {
throw new BusinessException('Morph type error!');
}
return $class::where('id', $morphId)->first();
}
public static function getMorphClass(string $morphType): ?string
{
return Relation::$morphMap[$morphType] ?? null;
}
/**
* 验证评论类型是否存在
*
* @param string $morphType
* @return void
* @throws BusinessException
*/
public static function checkMorphType(string $morphType)
{
if (is_null(self::getMorphClass($morphType))) {
throw new BusinessException('Morph type error!');
}
}
public static function instanceOf(string $morphType, $instance): bool
{
return get_class($instance) === self::getMorphClass($morphType);
}
public static function getMorphTypeOfInstance(object $instance): string
{
return self::getMorphType(get_class($instance));
}
public static function getMorphType(string $class): string
{
$morphMap = Relation::$morphMap;
$morphMap = array_flip($morphMap);
return $morphMap[$class] ?? '';
}
}

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Helper;
class Param
{
public const EMPTY_VALUES = ['', null];
public const ZERO_VALUES = [0, '0', '', null];
/**
* params是否存在key键若key对应的值存在于emptyValues中则也认为不存在
*/
public static function has(array $params, $key, array $emptyValues = self::EMPTY_VALUES): bool
{
if (isset($params[$key])) {
foreach ($emptyValues as $value) {
if ($params[$key] === $value) {
return false;
}
}
return true;
}
return false;
}
public static function require(array $params, array $requireKeys): array
{
$requireParams = [];
foreach ($requireKeys as $key) {
$requireParams[$key] = $params[$key] ?? null;
}
return $requireParams;
}
public static function default(array $params, array $defaults): array
{
foreach ($defaults as $key => $value) {
if (!isset($params[$key])) {
$params[$key] = $value;
}
}
return $params;
}
public static function remove(array $params, array $removeKeys): array
{
foreach ($removeKeys as $key) {
if (isset($params[$key])) {
unset($params[$key]);
}
}
return $params;
}
}

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Hyperf\AsyncQueue\Driver\DriverFactory;
use Hyperf\Utils\ApplicationContext;
class Queue
{
private static $factory;
private static function getFactory()
{
if (is_null(self::$factory)) {
self::$factory = ApplicationContext::getContainer()->get(DriverFactory::class);
}
return self::$factory;
}
public static function push($jobClass, $params, int $delay = 0): bool
{
$job = new $jobClass($params);
return self::getFactory()->get($job->getQueueName())->push($job, $delay);
}
}

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Hyperf\Redis\RedisFactory;
use Hyperf\Redis\RedisProxy;
use Hyperf\Utils\ApplicationContext;
/**
* Redis辅助类
*/
class Redis
{
/**
* @var RedisFactory
*/
private static $factory;
private static function getFactory(): RedisFactory
{
if (is_null(self::$factory)) {
self::$factory = ApplicationContext::getContainer()->get(RedisFactory::class);
}
return self::$factory;
}
public static function getInstance($poolName = 'default'): RedisProxy
{
return self::getFactory()->get($poolName);
}
public static function __callStatic($method, $args)
{
return call_user_func_array([self::getInstance(), $method], $args);
}
}

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use App\Constants\ResultCode;
use Hyperf\Utils\Contracts\Arrayable;
class Result implements Arrayable
{
private $code;
private $message;
private $data = [];
public function __construct(int $code = ResultCode::SUCCESS, string $message = '', array $data = [])
{
$this->code = $code;
$this->message = empty($message) ? ResultCode::getMessage($code) : $message;
$this->data = $data;
}
public function getCode(): int
{
return $this->code;
}
public function setCode(int $code)
{
return $this->code = $code;
}
public function getMessage(): string
{
return $this->message;
}
public function setMessage(string $message)
{
return $this->message = empty($message) ? ResultCode::getMessage($this->code) : $message;
}
public function getData(): array
{
return $this->data;
}
public function setData(array $data)
{
return $this->data = $data;
}
public function toArray(): array
{
return [
'code' => $this->code,
'message' => $this->message,
'data' => new \ArrayObject($this->data),
];
}
public function __toString()
{
return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
}
}

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Hyperf\Utils\Str as BaseStr;
class Str extends BaseStr
{
const PASSPHRASE = 'O0IP563o9WjoogRNFGN4';
const CIPHER_ALGO = 'aes-256-cbc';
public static function getRandomString($length, $special = true)
{
$chars = array(
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9'
);
if($special){
$chars = array_merge($chars, array(
'!', '@', '#', '$', '?', '|', '{', '/', ':', ';',
'%', '^', '&', '*', '(', ')', '-', '_', '[', ']',
'}', '<', '>', '~', '+', '=', ',', '.'
));
}
$charsLen = count($chars) - 1;
shuffle($chars);
$password = '';
for($i=0; $i<$length; $i++){
$password .= $chars[mt_rand(0, $charsLen)];
}
return $password;
}
public static function encrypt(string $data)
{
$iv = '62dTsnuD1Ow68pR8';
return openssl_encrypt($data, self::CIPHER_ALGO, self::PASSPHRASE, 0, $iv);
}
public static function decrypt(string $password)
{
$iv = '62dTsnuD1Ow68pR8';
return openssl_decrypt($password, self::CIPHER_ALGO, self::PASSPHRASE, 0, $iv);
}
public static function parseUrl(string $url)
{
if (empty($url)) {
return $url;
}
$item = explode(':', $url);
if (!isset($item[0]) || in_array($item[0], ['http', 'https'])) {
return $url;
}
return self::getFileUrlViaStorage($item[1], $item[0]);
}
public static function getFileUrlViaStorage(string $url, string $storage): string
{
$domain = config('file.storage.' . $storage . '.domain');
$prefixPath = config('file.storage.' . $storage . '.prefix_path', '');
return $domain . $prefixPath . $url;
}
}

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace App\Helper;
use Carbon\Carbon;
class Time
{
public static function getDateList($start, $end)
{
$startTime = strtotime($start . ' 00:00:00');
$endTime = strtotime($end . ' 00:00:00');
$dateList = [];
while ($startTime <= $endTime) {
$dateList[] = date('Y-m-d', $startTime);
$startTime += 24 * 60 * 60;
}
return $dateList;
}
public static function dateTimeRange(array $dateRange, $withTime = true)
{
if (!isset($dateRange[0]) || !isset($dateRange[1]) || count($dateRange) != 2) {
throw new \Exception('dateTimeRange param is error');
}
$dateRangeWithTime = false;
if (
self::checkFormatTime($dateRange[0], 'Y-m-d') &&
self::checkFormatTime($dateRange[1], 'Y-m-d')
) {
$dateRangeWithTime = false;
} elseif (
self::checkFormatTime($dateRange[0], 'Y-m-d H:i:s') &&
self::checkFormatTime($dateRange[1], 'Y-m-d H:i:s')
) {
$dateRangeWithTime = true;
} else {
throw new \Exception('dateTimeRange format is error');
}
if ($withTime && !$dateRangeWithTime) {
return [$dateRange[0] . ' 00:00:00', $dateRange[1] . ' 23:59:59'];
} elseif (!$withTime && $dateRangeWithTime) {
return [date('Y-m-d', strtotime($dateRange[0])), date('Y-m-d', strtotime($dateRange[1]))];
}
return $dateRange;
}
public static function checkFormatTime($timeString, $format = 'Y-m-d H:i:s') {
$timestamp = strtotime($timeString);
if (!$timestamp) {
return false;
}
if(date($format, $timestamp) === $timeString) {
return true;
}
return false;
}
public static function getMonthLastDate($month)
{
$firstDate = $month . '-01';
return date('Y-m-d', strtotime($firstDate . ' +1 month -1 day'));
}
public static function getMonthDateRange($month)
{
$carbon = Carbon::parse($month);
return [
$carbon->firstOfMonth()->format('Y-m-d'),
$carbon->lastOfMonth()->format('Y-m-d')
];
}
public static function getLastMonth()
{
return Carbon::now()->subMonth()->format('Y-m');
}
public static function getNextMonth()
{
return Carbon::now()->addMonth()->format('Y-m');
}
}

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Job;
use App\Service\UserService;
class AfterLoginJob extends Job
{
public function handle()
{
$userId = (int)$this->params['user_id'] ?? 0;
$gameId = (int)$this->params['game_id'] ?? 0;
$loginTime = (int)$this->params['login_time'] ?? 0;
/**
* @var UserService $userService
*/
$userService = make(UserService::class);
$userService->afterLogin($userId, $gameId, $loginTime);
}
}

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Job;
use App\Service\UserService;
class AfterRegisterJob extends Job
{
public function handle()
{
$userId = $this->params['user_id'] ?? '';
/**
* @var UserService $userService
*/
$userService = make(UserService::class);
$userService->afterRegister($userId);
}
}

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Job;
use App\Service\PlayerRoleService;
class AfterSaveRoleJob extends Job
{
public function handle()
{
/**
* @var PlayerRoleService $playerRoleService
*/
$playerRoleService = make(PlayerRoleService::class);
$playerRoleService->save($this->params);
}
}

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Job;
use App\Service\SpendService;
class AfterSpendJob extends Job
{
public function handle()
{
$payOrderNumber = $this->params['pay_order_number'] ?? '';
/**
* @var SpendService $spendService
*/
$spendService = make(SpendService::class);
$spendService->afterSpend($payOrderNumber);
}
}

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Job;
use Hyperf\AsyncQueue\Job as BaseJob;
class Job extends BaseJob
{
/**
* @var array
*/
public $params;
/**
* @var array
*/
public $context;
protected $queueName = 'default';
public function __construct(array $params, array $context)
{
$this->context = $context;
$this->params = $this->prepare($params);
}
protected function prepare(array $params): array
{
return $params;
}
public function handle()
{
}
protected function getJobName()
{
return get_called_class();
}
public function getQueueName()
{
return $this->queueName;
}
}

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Listener;
use Hyperf\Database\Events\QueryExecuted;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\Arr;
use Hyperf\Utils\Str;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
/**
* @Listener
*/
class DbQueryExecutedListener implements ListenerInterface
{
/**
* @var LoggerInterface
*/
private $logger;
public function __construct(ContainerInterface $container)
{
$this->logger = $container->get(LoggerFactory::class)->get('sql');
}
public function listen(): array
{
return [
QueryExecuted::class,
];
}
/**
* @param QueryExecuted $event
*/
public function process(object $event)
{
if ($event instanceof QueryExecuted) {
$sql = $event->sql;
if (! Arr::isAssoc($event->bindings)) {
foreach ($event->bindings as $key => $value) {
$sql = Str::replaceFirst('?', "'{$value}'", $sql);
}
}
$this->logger->info(sprintf('[%s] %s', $event->time, $sql));
}
}
}

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace App\Listener;
use App\Helper\Macro\WhereHasIn;
use App\Helper\Macro\WhereHasMorphIn;
use App\Helper\Macro\WhereHasNotIn;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Relations\Relation;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Framework\Event\BootApplication;
/**
* @Listener
*/
class ModelPrepareListener implements ListenerInterface
{
public function listen(): array
{
return [
BootApplication::class,
];
}
public function process(object $event)
{
$this->morphMapRelation();
$this->macroWhereHasIn();
}
private function morphMapRelation()
{
Relation::morphMap([
]);
}
private function macroWhereHasIn()
{
Builder::macro('whereHasIn', function ($relationName, $callable = null) {
return (new WhereHasIn($this, $relationName, function ($nextRelation, $builder) use ($callable) {
if ($nextRelation) {
return $builder->whereHasIn($nextRelation, $callable);
}
if ($callable) {
return $builder->callScope($callable);
}
return $builder;
}))->execute();
});
Builder::macro('orWhereHasIn', function ($relationName, $callable = null) {
return $this->orWhere(function ($query) use ($relationName, $callable) {
return $query->whereHasIn($relationName, $callable);
});
});
Builder::macro('whereHasNotIn', function ($relationName, $callable = null) {
return (new WhereHasNotIn($this, $relationName, function ($nextRelation, $builder) use ($callable) {
if ($nextRelation) {
return $builder->whereHasNotIn($nextRelation, $callable);
}
if ($callable) {
return $builder->callScope($callable);
}
return $builder;
}))->execute();
});
Builder::macro('orWhereHasNotIn', function ($relationName, $callable = null) {
return $this->orWhere(function ($query) use ($relationName, $callable) {
return $query->whereHasNotIn($relationName, $callable);
});
});
Builder::macro('whereHasMorphIn', WhereHasMorphIn::make());
Builder::macro('orWhereHasMorphIn', function ($relation, $types, $callback = null) {
return $this->whereHasMorphIn($relation, $types, $callback, 'or');
});
}
}

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Listener;
use Hyperf\AsyncQueue\AnnotationJob;
use Hyperf\AsyncQueue\Event\AfterHandle;
use Hyperf\AsyncQueue\Event\BeforeHandle;
use Hyperf\AsyncQueue\Event\Event;
use Hyperf\AsyncQueue\Event\FailedHandle;
use Hyperf\AsyncQueue\Event\RetryHandle;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Hyperf\Logger\LoggerFactory;
/**
* @Listener
*/
class QueueHandleListener implements ListenerInterface
{
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var FormatterInterface
*/
protected $formatter;
public function __construct(LoggerFactory $loggerFactory, FormatterInterface $formatter)
{
$this->logger = $loggerFactory->get('queue');
$this->formatter = $formatter;
}
public function listen(): array
{
return [
AfterHandle::class,
BeforeHandle::class,
FailedHandle::class,
RetryHandle::class,
];
}
public function process(object $event)
{
if ($event instanceof Event && $event->message->job()) {
$job = $event->message->job();
$jobClass = get_class($job);
if ($job instanceof AnnotationJob) {
$jobClass = sprintf('Job[%s@%s]', $job->class, $job->method);
}
$date = date('Y-m-d H:i:s');
switch (true) {
case $event instanceof BeforeHandle:
$this->logger->info(sprintf('[%s] Processing %s.', $date, $jobClass));
break;
case $event instanceof AfterHandle:
$this->logger->info(sprintf('[%s] Processed %s.', $date, $jobClass));
break;
case $event instanceof FailedHandle:
$this->logger->error(sprintf('[%s] Failed %s.', $date, $jobClass));
$this->logger->error($this->formatter->format($event->getThrowable()));
break;
case $event instanceof RetryHandle:
$this->logger->warning(sprintf('[%s] Retried %s.', $date, $jobClass));
break;
}
}
}
}

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Model;
class BaseGame extends Model
{
protected $table = 'tab_base_game';
public function scopeHasChild($query, $game)
{
return $query->where(function ($q) use($game) {
$q->orWhere('android_game_id', $game->id)->orWhere('ios_game_id', $game->id);
});
}
}

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Model;
class Game extends Model
{
protected $table = 'tab_game';
public function gameSource()
{
return $this->hasOne(GameSource::class, 'game_id', 'id');
}
public function logo()
{
return $this->hasOne(Image::class, 'id', 'icon');
}
}

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Model;
class GameData extends Model
{
protected $table = 'tab_game_data';
public $timestamps = false;
protected $fillable = ['game_id', 'pay_amount'];
}

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Scopes\CommonTimeScopes;
use Hyperf\DbConnection\Model\Model as BaseModel;
abstract class Model extends BaseModel
{
use CommonTimeScopes;
public static function alias($alias = null)
{
$name = with(new static)->getTable();
if ($alias) {
$name .= ' as ' . $alias;
}
return $name;
}
public static function fromAlias($alias)
{
return static::from(static::alias($alias));
}
public function getRawAttribute($key)
{
if (!$key) {
return null;
}
return $this->attributes[$key] ?? null;
}
}

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Scopes\CommonPromoteScopes;
use App\Scopes\PlayerScopes;
class Player extends Model
{
use CommonPromoteScopes;
protected $table = 'tab_user_play';
public function promote()
{
return $this->belongsTo(Promote::class, 'promote_id', 'id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Model;
class PlayerRetention extends Model
{
protected $table = 'tab_player_retention';
public static $dayList = [1, 2, 3, 4, 5, 6, 7, 15, 30];
public function player()
{
return $this->belongsTo(Player::class, 'player_id', 'id');
}
}

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Scopes\CommonPromoteScopes;
use App\Scopes\PlayerRoleScopes;
use App\Helper\DeviceType;
class PlayerRole extends Model
{
use CommonPromoteScopes;
use PlayerRoleScopes;
protected $table = 'tab_user_play_info';
public $timestamps = false;
public function promote()
{
return $this->belongsTo(Promote::class, 'promote_id', 'id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
public function testingBinding()
{
return $this->hasOne(TestingBinding::class, 'role_key', 'role_key');
}
public function bindTestingBinding()
{
return $this->hasOne(TestingBinding::class, 'bind_role_key', 'role_key');
}
public function getDeviceTypeNameAttribute()
{
return DeviceType::getDeviceTypeText($this->sdk_version);
}
public function getBaseGameNameAttribute()
{
return strtr($this->game_name, ['(安卓版)' => '', '(苹果版)' => '']);
}
public static function generateUniqueCode($gameId, $userId, $roleId)
{
if (is_null($roleId)) {
$roleId = 'UNKNOW';
}
return substr(md5($gameId . '#' . $userId . '#' . $roleId), 8, 16);
}
}

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Scopes\PromoteScopes;
class Promote extends Model
{
use PromoteScopes;
protected $table = 'tab_promote';
}

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Model;
class PromoteCompany extends Model
{
protected $table = 'tab_promote_company';
}

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Model;
class RebateGift extends Model
{
protected $table = 'tab_rebate_gifts';
protected $casts = [
'config' => 'array',
];
public $typeDisplayNames = [
'A' => '单笔充值福利',
'B' => '月卡福利发放',
'C' => '累充福利发放',
'D' => '首充福利发放',
'E' => '单日累充福利发放',
'F' => '周卡福利发放',
'G' => '首次进游福利',
'H' => '签到福利发放',
'I' => '充值返利发放',
];
}

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Model;
class RebateOrder extends Model
{
protected $table = 'tab_rebate_orders';
}

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Model;
class RebateTimesSetting extends Model
{
protected $table = 'tab_rebate_times_setting';
}

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Model;
class Server extends Model
{
protected $table = 'tab_server';
public $timestamps = false;
}

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Model;
class ServerUnion extends Model
{
protected $table = 'tab_server_union';
public $timestamps = false;
}

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Model;
class SimulateSpend extends Model
{
protected $table = 'tab_simulate_spend';
}

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Helper\DeviceType;
class Spend extends Model
{
const STATUS_FAILED = 0;
const STATUS_SUCCESS = 1;
protected $table = 'tab_spend';
public static $payWays = [
-1 => '绑币',
0 => '平台币',
1 => '支付宝',
2 => '微信',
3 => '微信APP',
9 => '双乾支付',
15 => '双乾支付-快捷',
17 => '易宝支付'
];
public static $payStatusList = [
self::STATUS_FAILED => '失败',
self::STATUS_SUCCESS => '成功',
];
public function getPayWayTextAttribute()
{
return self::$payWays[$this->pay_way] ?? '未知';
}
public function getDeviceTypeNameAttribute()
{
return DeviceType::getDeviceTypeText($this->sdk_version);
}
public function getPayStatusTextAttribute()
{
return self::$payStatusList[$this->pay_status] ?? '未知';
}
public function promote()
{
return $this->belongsTo(Promote::class, 'promote_id', 'id');
}
public function isSuccess()
{
return $this->pay_status === self::STATUS_SUCCESS;
}
}

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Model;
class Tool extends Model
{
const STATUS_OPEN = 1;
protected $table = 'tab_tool';
private $configItems;
public $timestamps = false;
public static $groups = [
'sms' => ['sms_set', 'zhongwang', 'juhedata'],
];
public function get(string $name)
{
if ($this->configItems == null) {
$this->configItems = (json_decode($this->config, true) ?? []);
}
return $this->configItems[$name] ?? null;
}
public static function getActiveByGroup($group)
{
if (!isset(self::$groups[$group])) {
return null;
}
return static::where('status', self::STATUS_OPEN)->whereIn('name', self::$groups[$group])->first();
}
}

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Scopes\CommonPromoteScopes;
use App\Scopes\UserScopes;
class User extends Model
{
use CommonPromoteScopes;
use UserScopes;
protected $table = 'tab_user';
public function promote()
{
return $this->belongsTo(Promote::class, 'promote_id', 'id');
}
}

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Model;
class UserData extends Model
{
protected $table = 'tab_user_data';
public $timestamps = false;
protected $fillable = ['user_id', 'pay_amount'];
}

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Model;
class UserPlayDataCount extends Model
{
protected $table = 'tab_user_play_data_count';
}

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Process;
use Hyperf\AsyncQueue\Process\ConsumerProcess;
use Hyperf\Process\Annotation\Process;
/**
* @Process
*/
class AsyncQueueConsumer extends ConsumerProcess
{
}

@ -0,0 +1,24 @@
<?php
namespace App\Scopes;
trait CommonPromoteScopes
{
/**
* 查询属于某个推广员的玩家
* @param $query
* @param $promote 推广员
* @param $containSubPromotes 是否包含所有下级
*/
public function scopeOfPromote($query, $promote, $containSubPromotes = true)
{
if ($containSubPromotes) {
$query->whereHas('promote', function ($query) use ($promote) {
$query->ofParent($promote)->orWhere('promote_id', $promote->id);
});
} else {
$query->where('promote_id', $promote->id);
}
return $query;
}
}

@ -0,0 +1,35 @@
<?php
namespace App\Scopes;
use App\Helper\Time;
trait CommonTimeScopes
{
public function scopeRangeTime($query, $column, $timeRange, $withTime = true)
{
if (!$timeRange) {
return $query;
}
$timeRange = Time::dateTimeRange($timeRange, $withTime);
return $query->whereBetween($column, [strtotime($timeRange[0]), strtotime($timeRange[1])]);
}
public function scopeOfMonth($query, $column, $month, $withTime = true)
{
if (!$month) {
return $query;
}
$firstDate = $month . '-01';
$lastDate = Time::getMonthLastDate($month);
$timeRange = Time::dateTimeRange([$firstDate, $lastDate], $withTime);
return $query->whereBetween($column, [strtotime($timeRange[0]), strtotime($timeRange[1])]);
}
public function scopeTimeOverlay($query, $rangeColumns, $timeRange)
{
return $query->whereNot(function ($q) use ($rangeColumns, $timeRange) {
$q->where($rangeColumns[1], '<', $timeRange[0])->orWhere($rangeColumns[0], '<', $timeRange[1]);
});
}
}

@ -0,0 +1,19 @@
<?php
namespace App\Scopes;
trait GameScopes
{
public function scopeOfConditions($query, $params)
{
if (!empty($params['relation_game_id'])) {
$query->where('relation_game_id', $params['relation_game_id']);
}
if (!empty($params['base_game_id'])) {
$query->where('base_game_id', $params['base_game_id']);
}
if (!empty($params['sdk_version'])) {
$query->where('sdk_version', $params['sdk_version']);
}
}
}

@ -0,0 +1,53 @@
<?php
namespace App\Scopes;
trait PlayerRoleScopes
{
public function scopeOfConditions($query, $params)
{
if (!empty($params['server_id'])) {
$query->where('server_id', $params['server_id']);
}
if (!empty($params['sdk_version'])) {
$query->where('sdk_version', $params['sdk_version']);
}
if (!empty($params['game_id'])) {
$query->where('game_id', $params['game_id']);
}
if (!empty($params['role_name'])) {
$query->where('role_name', $params['role_name']);
}
if (!empty($params['server_name'])) {
$query->where('server_name', $params['server_name']);
}
if (!empty($params['user_account'])) {
$query->where('user_account', $params['user_account']);
}
if (!empty($params['role_id'])) {
$query->where('role_id', $params['role_id']);
}
}
public function scopeRangeRoleLevel($query, ?array $roleLevelRange)
{
if (is_null($roleLevelRange)) {
return $query;
}
$min = $roleLevelRange[0] ?? null;
$min == '' ? null : $min;
$max = $roleLevelRange[1] ?? null;
$max == '' ? null : $max;
if (!is_null($min)) {
$query->where('role_level', '>=', $min);
}
if (!is_null($max)) {
$query->where('role_level', '<=', $max);
}
return $query;
}
}

@ -0,0 +1,8 @@
<?php
namespace App\Scopes;
trait PlayerScopes
{
}

@ -0,0 +1,39 @@
<?php
namespace App\Scopes;
trait PromoteScopes
{
public function scopeOfConditions($query, $params)
{
if (!empty($params['level'])) {
$query->where('level', $params['level']);
}
if (!empty($params['parent_id'])) {
$query->where('parent_id', $params['parent_id']);
}
if (!empty($params['account'])) {
$query->where('account', 'like', '%' . $params['account'] . '%');
}
if (!empty($params['mobile'])) {
$query->where('mobile_phone', 'like', '%' . $params['mobile'] . '%');
}
if (!empty($params['idcard'])) {
$query->where('idcard', 'like', '%' . $params['idcard'] . '%');
}
if (!empty($params['realname'])) {
$query->where('real_name', 'like', '%' . $params['realname'] . '%');
}
if (isset($params['status']) && $params['status'] != -1) {
$query->where('status', $params['status']);
}
}
/**
* 查找子推广账号
*/
public function scopeOfParent($query, $promote)
{
return $query->where('chain', 'like', $promote->chain . $promote->id . '/%');
}
}

@ -0,0 +1,49 @@
<?php
namespace App\Scopes;
use App\Model\Promote;
trait SpendScopes
{
public function scopeOfConditions($query, $params)
{
if (!empty($params['server_id'])) {
$query->where('server_id', $params['server_id']);
}
if (!empty($params['server_name'])) {
$query->where('server_name', $params['server_name']);
}
if (!empty($params['game_id'])) {
$query->where('game_id', $params['game_id']);
}
if (!empty($params['game_name'])) {
$query->where('game_name', $params['game_name']);
}
if (isset($params['pay_status']) && $params['pay_status'] != -1) {
$query->where('pay_status', $params['pay_status']);
}
if (isset($params['pay_way']) && $params['pay_way'] != -99) {
$query->where('pay_way', $params['pay_way']);
}
if (!empty($params['pay_order_number'])) {
$query->where('pay_order_number', $params['pay_order_number']);
}
if (!empty($params['user_account'])) {
$query->where('user_account', $params['user_account']);
}
if (!empty($params['role_name'])) {
$query->where('game_player_name', $params['game_player_name']);
}
return $query;
}
public function scopeSettleOrder($query, string $tableAlias = null)
{
$column = 'is_check';
if ($tableAlias) {
$column = $tableAlias . '.' . $column;
}
return $query->where($column, 1);
}
}

@ -0,0 +1,17 @@
<?php
namespace App\Scopes;
trait UserScopes
{
public function scopeOfConditions($query, $params)
{
if (!empty($params['account'])) {
$query->where('account', $params['account']);
}
if (!empty($params['id'])) {
$query->where('id', $params['id']);
}
return $query;
}
}

@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Exception\BusinessException;
use App\Model\Game;
use App\Model\PlayerRole;
use App\Model\Server;
use App\Model\ServerUnion;
use App\Model\User;
class PlayerRoleService extends Service
{
public function save($params)
{
$userId = $params['user_id'] ?? 0;
$gameId = $params['game_id'] ?? 0;
$serverId = $params['server_id'] ?? 0;
$serverName = $params['server_name'] ?? 0;
$roleName = $params['game_player_name'] ?? 0;
$roleId = $params['game_player_id'] ?? 0;
$roleLevel = $params['role_level'] ?? 0;
$time = $params['time'] ?? 0;
$clientIp = $params['client_ip'] ?? '';
$sdkVersion = $params['sdk_version'] ?? 0;
$user = User::where('id', $userId)->first(['id', 'account', 'nickname', 'puid', 'promote_id', 'promote_account']);
if ($user == null) {
throw new BusinessException('用户不存在');
}
$game = Game::where('id', $gameId)->first(['id', 'game_name', 'sdk_version']);
if ($game == null) {
throw new BusinessException('游戏不存在');
}
$serverInfo = ['server_id' => $serverId, 'server_name' => $serverName];
$roleInfo = ['role_id' => $roleId, 'role_name' => $roleName, 'role_level' => $roleLevel];
$this->saveRole($game, $user, $serverInfo, $roleInfo, $time, $clientIp);
$this->saveServer($serverInfo, $game, $time);
$this->saveServerUnion($serverInfo, $game, $time);
}
private function saveRole(
Game $game,
User $user,
array $serverInfo,
array $roleInfo,
int $time = 0,
string $clientIp = ''
) {
$role = PlayerRole::where('user_id', $user->id)
->where('game_id', $game->id)
->where('server_id', $serverInfo['server_id'])
->where('role_id', $roleInfo['role_id'])
->first();
if ($role == null) {
$role = new PlayerRole();
$role->create_time = $time;
$role->create_ip = $clientIp;
$role->promote_id = $user->promote_id;
$role->promote_account = $user->promote_account;
}
$role->game_id = $game->id;
$role->game_name = $game->game_name;
$role->server_id = $serverInfo['server_id'];
$role->server_name = $serverInfo['server_name'];
$role->role_id = $roleInfo['role_id'];
$role->role_name = $roleInfo['role_name'];
$role->role_level = $roleInfo['role_level'];
$role->game_role_id = $game->id . '#' . $roleInfo['role_id'];;
$role->user_id = $user->id;
$role->user_account = $user->account;
$role->user_nickname = $user->nickname;
$role->play_time = $time;
$role->sdk_version = $game->sdk_version;
$role->play_ip = $clientIp;
$role->pipuid = $user->puid;
$role->save();
}
private function saveServer(array $serverInfo, Game $game, int $time = 0)
{
$serverName = $serverInfo['server_name'] ?? '';
$serverId = $serverInfo['server_id'] ?? 0;
$server = Server::where('game_id', $game->id)->where('server_id', $serverId)->first();
if ($server == null) {
$server = new Server();
$server->game_id = $game->id;
$server->game_name = $game->game_name;
$server->server_name = $serverName;
$server->server_num = $serverId;
$server->server_id = $serverId;
$server->server_version = $game->sdk_version;
$server->create_time = $time;
$server->start_time = $time;
$server->save();
} else {
if ($server->server_name != $serverName) {
$server->server_name = $serverName;
$server->save();
}
}
}
private function saveServerUnion(array $serverInfo, Game $game, int $time = 0)
{
$serverName = $serverInfo['server_name'] ?? '';
$serverId = $serverInfo['server_id'] ?? 0;
$server = Server::where('server_name', $serverName)->where('game_id', $game->id)->first(['id']);
if ($server) {
return;
}
$serverUnion = ServerUnion::where('server_name', $serverName)->where('game_id', $game->id)->first(['id']);
if ($serverUnion == null) {
$serverUnion = new ServerUnion();
$serverUnion->game_id = $game->id;
$serverUnion->game_name = $game->game_name;
$serverUnion->server_name = $serverName;
$serverUnion->server_num = $serverId;
$serverUnion->server_id = $serverId;
$serverUnion->server_version = $game->sdk_version;
$serverUnion->create_time = $time;
$serverUnion->start_time = $time;
$serverUnion->save();
}
}
}

@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Helper\Time;
use App\Model\Player;
use App\Model\PlayerRetention;
use App\Model\Promote;
use App\Model\PromoteCompany;
use Hyperf\DbConnection\Db;
class PlayerService extends Service
{
public function getPlayerRetention(array $params): array
{
$startTime = $params['start_time'] ?? date('Y-m-d');
$endTime = $params['end_time'] ?? date('Y-m-d');
$gameIds = $params['game_ids'] ?? [];
$promoteId = $params['promote_id'] ?? 0;
$companyId = $params['company_id'] ?? 0;
$gameId = $params['game_id'] ?? 0;
if (empty($gameIds) && $gameId) {
$gameIds = [$gameId];
}
$promote = null;
$promoteCompany = null;
if ($promoteId) {
$promote = Promote::where('id', $promoteId)->first(['id', 'chain', 'parent_id']);
} elseif ($companyId) {
$promoteCompany = PromoteCompany::where('id', $promoteId)->first(['id']);
}
$dateList = Time::getDateList($startTime, $endTime);
$startTime = strtotime($startTime . ' 00:00:00');
$endTime = strtotime($endTime . ' 23:59:59');
$dailyData = $this->getDailyRetention($startTime, $endTime, $gameIds, $promote, $promoteCompany);
$dailyRecords = [];
foreach ($dailyData as $item) {
$dailyRecords[$item->date] = $item;
}
$query = Player::select(DB::raw('FROM_UNIXTIME(create_time, "%Y-%m-%d") date'), DB::raw('count(*) count'))
->whereBetween('create_time', [$startTime, $endTime])
->whereIn('game_id', $gameIds);
if ($promote) {
$query->ofPromote($promote, true);
} elseif ($promoteCompany) {
$query->ofPromoteCompany($promoteCompany);
}
$countList = $query->groupBy('date')->get()->pluck('count', 'date');
$records = [];
foreach ($dateList as $date) {
$record = [
'date' => $date,
'register_count' => isset($countList[$date]) ? $countList[$date] : 0,
];
foreach (PlayerRetention::$dayList as $day) {
$item = $dailyRecords[$date] ?? null;
$record['retention_day' . $day] = $item ? $item->{'day' . $day} : 0;
}
$records[] = $record;
}
return $records;
}
public function getDailyRetention($start, $end, $gameIds, $promote = null, $promoteCompany = null)
{
$columns = [
'FROM_UNIXTIME(b.create_time, "%Y-%m-%d") date',
'sum(day1) day1',
'sum(day2) day2',
'sum(day3) day3',
'sum(day4) day4',
'sum(day5) day5',
'sum(day6) day6',
'sum(day7) day7',
'sum(day15) day15',
'sum(day30) day30'
];
$query = PlayerRetention::fromAlias('a')
->select(Db::raw(implode(',', $columns)))
->leftJoin(Player::alias('b'), function ($join) {
$join->on('a.player_id', '=', 'b.id');
})
->whereBetween('b.create_time', [$start, $end])
->whereIn('a.game_id', $gameIds)
->groupBy('date');
$promoteTable = Promote::alias('c');
if ($promote) {
$query->leftJoin($promoteTable, function ($join) {
$join->on('b.promote_id', '=', 'c.id');
})->whereRaw('(c.chain like "'. $promote->chain . $promote->id . '/%" or c.id=' . $promote->id . ')');
} elseif ($promoteCompany) {
$query->leftJoin($promoteTable, function ($join) {
$join->on('b.promote_id', '=', 'c.id');
})->whereRaw('c.company_id = ' . $promoteCompany->id);
}
return $query->get();
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Service;
class Service
{
}

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Exception\BusinessException;
use App\Model\GameData;
use App\Model\Spend;
use App\Model\UserData;
use App\Model\UserPlayDataCount;
use App\Helper\Log;
use Hyperf\DbConnection\Db;
class SpendService extends Service
{
public function afterSpend($payOrderNumber)
{
Log::info('[GAME-RECHARGE]产生充值订单:' . $payOrderNumber);
$spend = Spend::where('pay_order_number', $payOrderNumber)->first();
if (!$spend) {
throw new BusinessException('订单不存在');
}
if ($spend->pay_status != 1) {
throw new BusinessException('订单支付未成功');
}
if ($spend->event_status == 1) {
throw new BusinessException('该订单已处理');
}
$this->saveUserPlayDataCountBySpend($spend);
$this->addUserDataPayAmount($spend);
$this->addGameDataPayAmount($spend);
$spend->event_status = 1;
$spend->save();
}
private function saveUserPlayDataCountBySpend(Spend $spend)
{
$date = strtotime(date('Y-m-d 00:00:00', $spend->pay_time));
$record = UserPlayDataCount::where('user_id', $spend->user_id)
->where('game_id', $spend->game_id)
->where('server_id', $spend->server_id)
->where('role_id', $spend->game_player_id)
->where('create_time', $date)
->first();
if (!$record) {
$record = new UserPlayDataCount();
$record->user_id = $spend->user_id;
$record->promote_id = $spend->promote_id;
$record->game_id = $spend->game_id;
$record->server_id = $spend->server_id;
$record->role_id = $spend->game_player_id;
$record->create_time = $date;
$record->recharge_cost = $spend->pay_amount;
$record->recharge_count = 1;
} else {
$record->recharge_cost = Db::raw('recharge_cost+' . $spend->pay_amount);
$record->recharge_count = Db::raw('recharge_count+1');
}
$record->save();
}
private function addUserDataPayAmount($spend)
{
UserData::updateOrCreate(
['user_id' => $spend->user_id],
['pay_amount' => Db::raw('pay_amount+' . $spend->pay_amount)]
);
}
private function addGameDataPayAmount($spend)
{
GameData::updateOrCreate(
['game_id' => $spend->game_id],
['pay_amount' => Db::raw('pay_amount+' . $spend->pay_amount)]
);
}
}

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Exception\BusinessException;
use App\Model\Player;
use App\Model\PlayerRetention;
use App\Model\User;
use App\Helper\Log;
class UserService extends Service
{
public function afterLogin($gameId, $userId, $loginTime = 0)
{
if (empty($time)) {
$loginTime = time();
}
Log::info('[USER-LOGIN]用户['. $userId . ']登录游戏[' . $gameId . ']');
$player = Player::where('user_id', $userId)->where('game_id', $gameId)->first(['id', 'create_time']);
if (!$player) {
throw new BusinessException('玩家信息不存在');
}
$playerRetention = PlayerRetention::where('player_id', $player->id)->first();
$retentionDay = $this->getRetentionDay($player->create_time, $loginTime);
if ($retentionDay > 0) {
if (!$playerRetention) {
$playerRetention = new PlayerRetention();
$playerRetention->player_id = $player->id;
$playerRetention->game_id = $gameId;
$playerRetention->user_id = $userId;
}
$playerRetention->{'day' . $retentionDay} = 1;
$playerRetention->save();
}
}
private function getRetentionDay($createTime, $loginTime)
{
$dayTime = 24 * 60 * 60;
$retentionDay = 0;
foreach (PlayerRetention::$dayList as $day) {
$date = date('Ymd', $createTime + $day * $dayTime);
if ($date == date('Ymd', $loginTime)) {
$retentionDay = $day;
continue;
}
}
return $retentionDay;
}
public function afterRegister($userId)
{
Log::info('[USER-REGISTER]用户['. $userId . ']注册');
$user = User::where('id', $userId)->first(['device_number', 'register_ip', 'id']);
if (!$user) {
throw new BusinessException('用户不存在');
}
$repeatUser = User::where('device_number', $user->device_number)
->where('register_ip', $user->register_ip)
->where('id', '<', $user->id)
->first();
if ($repeatUser) {
$user->is_repeat = 1;
$user->save();
Log::info('[USER-REGISTER]用户['. $userId . ']为重复账号');
}
}
}

@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');
ini_set('memory_limit', '1G');
error_reporting(E_ALL);
date_default_timezone_set('Asia/Shanghai');
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
require BASE_PATH . '/vendor/autoload.php';
// Self-called anonymous function that creates its own scope and keep the global namespace clean.
(function () {
Hyperf\Di\ClassLoader::init();
/** @var Psr\Container\ContainerInterface $container */
$container = require BASE_PATH . '/config/container.php';
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();
})();

@ -0,0 +1,89 @@
{
"name": "hyperf/hyperf-skeleton",
"type": "project",
"keywords": [
"php",
"swoole",
"framework",
"hyperf",
"microservice",
"middleware"
],
"description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.",
"license": "Apache-2.0",
"require": {
"php": ">=7.3",
"hyperf/async-queue": "~2.2.0",
"hyperf/cache": "~2.2.0",
"hyperf/command": "~2.2.0",
"hyperf/config": "~2.2.0",
"hyperf/constants": "~2.2.0",
"hyperf/database": "~2.2.0",
"hyperf/db-connection": "~2.2.0",
"hyperf/framework": "~2.2.0",
"hyperf/guzzle": "~2.2.0",
"hyperf/http-server": "~2.2.0",
"hyperf/json-rpc": "~2.2.0",
"hyperf/logger": "~2.2.0",
"hyperf/memory": "~2.2.0",
"hyperf/model-cache": "~2.2.0",
"hyperf/process": "~2.2.0",
"hyperf/redis": "~2.2.0",
"hyperf/rpc": "~2.2.0",
"hyperf/rpc-client": "~2.2.0",
"hyperf/rpc-server": "~2.2.0",
"hyperf/validation": "^2.2",
"phpoffice/phpspreadsheet": "^1.24"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"hyperf/devtool": "~2.2.0",
"hyperf/ide-helper": "~2.2.0",
"hyperf/testing": "~2.2.0",
"mockery/mockery": "^1.0",
"phpstan/phpstan": "^0.12",
"swoole/ide-helper": "^4.5"
},
"suggest": {
"ext-openssl": "Required to use HTTPS.",
"ext-json": "Required to use JSON.",
"ext-pdo": "Required to use MySQL Client.",
"ext-pdo_mysql": "Required to use MySQL Client.",
"ext-redis": "Required to use Redis Client."
},
"autoload": {
"psr-4": {
"App\\": "app/"
},
"files": [
"app/Helper/Functions.php"
]
},
"autoload-dev": {
"psr-4": {
"HyperfTest\\": "./test/"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"extra": [],
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-autoload-dump": [
"rm -rf runtime/container"
],
"test": "co-phpunit --prepend test/bootstrap.php -c phpunit.xml --colors=always",
"cs-fix": "php-cs-fixer fix $1",
"analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config",
"start": [
"Composer\\Config::disableProcessTimeout",
"php ./bin/hyperf.php start"
]
}
}

9278
composer.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'scan' => [
'paths' => [
BASE_PATH . '/app',
],
'ignore_annotations' => [
'mixin',
],
],
];

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'default' => [
'driver' => Hyperf\AsyncQueue\Driver\RedisDriver::class,
'redis' => [
'pool' => 'default',
],
'channel' => '{queue}',
'timeout' => 2,
'retry_seconds' => 5,
'handle_timeout' => 10,
'processes' => 1,
'concurrent' => [
'limit' => 10,
],
],
];

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'default' => [
'driver' => Hyperf\Cache\Driver\RedisDriver::class,
'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class,
'prefix' => 'c:',
],
];

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
return [
'lanhuo' => [
'base_url' => env('LANHUO_URL', 'https://c.lshygame.com'),
'app_id' => 'LLWF71700000001644',
'app_key' => '31h8a',
'app_secret' => '9b0c2130ea023243ff76bf7bc724cfc2',
],
];

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'default' => [
'driver' => env('DB_DRIVER', 'mysql'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_DATABASE', 'hyperf'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => env('DB_PREFIX', ''),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
],
'cache' => [
'handler' => Hyperf\ModelCache\Handler\RedisHandler::class,
'cache_key' => '{mc:%s:m:%s}:%s:%s',
'prefix' => 'default',
'ttl' => 3600 * 24,
'empty_model_ttl' => 600,
'load_script' => true,
],
'commands' => [
'gen:model' => [
'path' => 'app/Model',
'force_casts' => true,
'inheritance' => 'Model',
'uses' => '',
'table_mapping' => [],
],
],
],
];

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'generator' => [
'amqp' => [
'consumer' => [
'namespace' => 'App\\Amqp\\Consumer',
],
'producer' => [
'namespace' => 'App\\Amqp\\Producer',
],
],
'aspect' => [
'namespace' => 'App\\Aspect',
],
'command' => [
'namespace' => 'App\\Command',
],
'controller' => [
'namespace' => 'App\\Controller',
],
'job' => [
'namespace' => 'App\\Job',
],
'listener' => [
'namespace' => 'App\\Listener',
],
'middleware' => [
'namespace' => 'App\\Middleware',
],
'Process' => [
'namespace' => 'App\\Processes',
],
],
];

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'handler' => [
'http' => [
Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
App\Exception\Handler\AppExceptionHandler::class,
],
],
];

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
$logPath = rtrim(env('LOG_PATH', '@runtime'), '/');
$logPath = $logPath === '@runtime' ? BASE_PATH . '/runtime/logs/' : $logPath;
$logFileName = $logPath . '/' . env('LOG_FILE_NAME', env('APP_NAME', 'hyperf') . '.log');
/* return [
'default' => [
'handler' => [
'class' => \Monolog\Handler\StreamHandler::class,
'constructor' => [
'stream' => $logFileName,
'level' => \Monolog\Logger::DEBUG,
],
],
'formatter' => [
'class' => \Monolog\Formatter\LineFormatter::class,
'constructor' => [
'format' => null,
'dateFormat' => null,
'allowInlineLineBreaks' => true,
]
],
],
]; */
return [
'default' => [
'handler' => [
'class' => Monolog\Handler\RotatingFileHandler::class,
'constructor' => [
'filename' => $logFileName,
'maxFiles' => env('LOG_MAX_FILES', 7),
'level' => Monolog\Logger::DEBUG,
],
],
'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [
'format' => null,
'dateFormat' => 'Y-m-d H:i:s',
'allowInlineLineBreaks' => true,
],
],
],
];

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save