校区服务

This commit is contained in:
Aether
2025-09-18 15:54:38 +08:00
parent 0920cef866
commit 30e00c5949
15 changed files with 486 additions and 240 deletions

12
.env
View File

@@ -1,15 +1,15 @@
APP_NAME=hyperf- APP_NAME=hyperf-data
APP_ENV=dev APP_ENV=dev
DB_DRIVER=mysql DB_DRIVER=mysql
DB_HOST=localhost DB_HOST=localhost
DB_PORT=3306 DB_PORT=3306
DB_DATABASE=hyperf_ DB_DATABASE=hyperf_data
DB_USERNAME=hyperf_ DB_USERNAME=hyperf_data
DB_PASSWORD=4cfDRXZSksn7npiP DB_PASSWORD=4cfDRXZSksn7npiP
DB_CHARSET=utf8mb4 DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci DB_COLLATION=utf8mb4_unicode_ci
DB_PREFIX=_ DB_PREFIX=da_
REDIS_HOST=localhost REDIS_HOST=localhost
REDIS_AUTH=(null) REDIS_AUTH=(null)
@@ -22,4 +22,8 @@ SWOOLE_WORKER_NUM=1
NACOS_HOST=192.168.28.199 NACOS_HOST=192.168.28.199
NACOS_PORT=8848 NACOS_PORT=8848
NACOS_USERNAME=nacos
NACOS_PASSWORD=nacos
NACOS_NAMESPACE=e42b853c-5195-478b-b5e3-6d49f6a45053 NACOS_NAMESPACE=e42b853c-5195-478b-b5e3-6d49f6a45053
NACOS_GROUP=DEFAULT_GROUP
NACOS_SERVICE_NAME=hyperf-data

View File

@@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Controller;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerInterface;
abstract class AbstractController
{
#[Inject]
protected ContainerInterface $container;
#[Inject]
protected RequestInterface $request;
#[Inject]
protected ResponseInterface $response;
}

View File

@@ -1,27 +0,0 @@
<?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}.",
];
}
}

View File

@@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Exception\Handler;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class AppExceptionHandler extends ExceptionHandler
{
public function __construct(protected StdoutLoggerInterface $logger)
{
}
public function handle(Throwable $throwable, ResponseInterface $response)
{
$this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
$this->logger->error($throwable->getTraceAsString());
return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
}
public function isValid(Throwable $throwable): bool
{
return true;
}
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\JsonRpc\Contract;
interface CampusServiceInterface
{
/**
* 获取单个校区详情.
* @param int $id 校区ID
*/
public function getCampus(int $id): array;
/**
* 批量获取校区信息.
* @param array $ids 校区ID列表
*/
public function batchGetCampus(array $ids): array;
/**
* 按省份获取校区列表.
* @param string $province 省份名称
*/
public function getCampusByProvince(string $province): array;
/**
* 按城市获取校区列表.
* @param string $province 省份
* @param string $city 城市
*/
public function getCampusByCity(string $province, string $city): array;
/**
* 获取校区完整层级路径.
* @param int $campusId 校区ID
* @return array 如:[省份, 城市, 校区]
*/
public function getCampusHierarchy(int $campusId): array;
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace App\JsonRpc\Service;
use App\JsonRpc\Contract\CampusServiceInterface;
use App\Model\Campus;
use Hyperf\RpcServer\Annotation\RpcService;
use RuntimeException;
#[RpcService(
name: 'DataCampus',
server: 'jsonrpc-http',
protocol: 'jsonrpc-http',
publishTo: 'nacos'
)]
class CampusService implements CampusServiceInterface
{
public function getCampus(int $id): array
{
$campus = Campus::find($id);
if (! $campus) {
throw new RuntimeException("校区不存在: {$id}");
}
return $campus->toArray();
}
public function batchGetCampus(array $ids): array
{
if (empty($ids)) {
return [];
}
$campuses = Campus::whereIn('id', $ids)->get()->toArray();
return array_column($campuses, null, 'id');
}
public function getCampusByProvince(string $province): array
{
return Campus::province($province)
->level(3)
->get()
->toArray();
}
public function getCampusByCity(string $province, string $city): array
{
return Campus::province($province)
->city($city)
->level(3)
->get()
->toArray();
}
public function getCampusHierarchy(int $campusId): array
{
$hierarchy = [];
$current = Campus::find($campusId);
if (! $current) {
return $hierarchy;
}
// 从校区向上追溯层级
while ($current) {
array_unshift($hierarchy, [
'id' => $current->id,
'name' => $current->name,
'level' => $current->level,
'province' => $current->province,
'city' => $current->city,
]);
if ($current->parent_id === 0) {
break;
}
$current = Campus::find($current->parent_id);
}
return $hierarchy;
}
}

View File

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

View File

@@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Listener;
use Hyperf\Command\Event\AfterExecute;
use Hyperf\Coordinator\Constants;
use Hyperf\Coordinator\CoordinatorManager;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
#[Listener]
class ResumeExitCoordinatorListener implements ListenerInterface
{
public function listen(): array
{
return [
AfterExecute::class,
];
}
public function process(object $event): void
{
CoordinatorManager::until(Constants::WORKER_EXIT)->resume();
}
}

80
app/Model/Campus.php Normal file
View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Aether\AetherModel;
use Carbon\Carbon;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Relations\HasMany;
/**
* 校区模型(支持层级结构).
* @property int $id
* @property string $name 校区名称
* @property int $parent_id 父级ID
* @property int $level 层级1-省份2-地级市3-校区
* @property string $province 省份
* @property string $city 地级市
* @property string $address 详细地址
* @property string $contact_phone 联系电话
* @property string $contact_person 联系人
* @property int $status 状态0-禁用1-启用
* @property Carbon $created_at
* @property Carbon $updated_at
* @property Carbon $deleted_at
*/
class Campus extends AetherModel
{
protected ?string $table = 'campus';
protected array $fillable = [
'name',
'parent_id',
'level',
'province',
'city',
'address',
'contact_phone',
'status',
];
protected array $casts = [
'parent_id' => 'integer',
'level' => 'integer',
'status' => 'integer',
];
/**
* 获取子校区.
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id', 'id');
}
/**
* 按省份查询.
*/
public function scopeProvince(Builder $query, string $province): Builder
{
return $query->where('province', $province);
}
/**
* 按城市查询.
*/
public function scopeCity(Builder $query, string $city): Builder
{
return $query->where('city', $city);
}
/**
* 按层级查询.
*/
public function scopeLevel(Builder $query, int $level): Builder
{
return $query->where('level', $level);
}
}

View File

@@ -16,6 +16,7 @@
"hyperf/cache": "~3.1.0", "hyperf/cache": "~3.1.0",
"hyperf/command": "~3.1.0", "hyperf/command": "~3.1.0",
"hyperf/config": "~3.1.0", "hyperf/config": "~3.1.0",
"hyperf/config-nacos": "^3.1",
"hyperf/database": "~3.1.0", "hyperf/database": "~3.1.0",
"hyperf/db-connection": "~3.1.0", "hyperf/db-connection": "~3.1.0",
"hyperf/engine": "^2.10", "hyperf/engine": "^2.10",
@@ -32,7 +33,9 @@
"hyperf/rpc": "~3.1.0", "hyperf/rpc": "~3.1.0",
"hyperf/rpc-client": "~3.1.0", "hyperf/rpc-client": "~3.1.0",
"hyperf/rpc-server": "~3.1.0", "hyperf/rpc-server": "~3.1.0",
"hyperf/service-governance": "^3.1",
"hyperf/service-governance-nacos": "^3.1", "hyperf/service-governance-nacos": "^3.1",
"hyperf/utils": "^3.1",
"hyperf/validation": "^3.1" "hyperf/validation": "^3.1"
}, },
"require-dev": { "require-dev": {

212
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d60c21c4bb08e5b1d7552c9d0bbe3b78", "content-hash": "c30923a4e3d3314c7a9c295b398631d8",
"packages": [ "packages": [
{ {
"name": "carbonphp/carbon-doctrine-types", "name": "carbonphp/carbon-doctrine-types",
@@ -1345,6 +1345,150 @@
], ],
"time": "2025-06-06T02:41:30+00:00" "time": "2025-06-06T02:41:30+00:00"
}, },
{
"name": "hyperf/config-center",
"version": "v3.1.60",
"source": {
"type": "git",
"url": "https://github.com/hyperf/config-center.git",
"reference": "89f42313140cb8957e43a23d089ab46208e87a9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hyperf/config-center/zipball/89f42313140cb8957e43a23d089ab46208e87a9c",
"reference": "89f42313140cb8957e43a23d089ab46208e87a9c",
"shasum": ""
},
"require": {
"hyperf/contract": "~3.1.0",
"hyperf/coordinator": "~3.1.0",
"hyperf/coroutine": "~3.1.0",
"hyperf/support": "~3.1.0",
"php": ">=8.1"
},
"suggest": {
"hyperf/process": "^2.1"
},
"type": "library",
"extra": {
"hyperf": {
"config": "Hyperf\\ConfigCenter\\ConfigProvider"
},
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Hyperf\\ConfigCenter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "The abstraction component of config center",
"homepage": "https://hyperf.io",
"keywords": [
"config-center",
"hyperf",
"php"
],
"support": {
"docs": "https://hyperf.wiki",
"issues": "https://github.com/hyperf/hyperf/issues",
"pull-request": "https://github.com/hyperf/hyperf/pulls",
"source": "https://github.com/hyperf/hyperf"
},
"funding": [
{
"url": "https://hyperf.wiki/#/zh-cn/donate",
"type": "custom"
},
{
"url": "https://opencollective.com/hyperf",
"type": "open_collective"
}
],
"time": "2025-07-07T02:01:34+00:00"
},
{
"name": "hyperf/config-nacos",
"version": "v3.1.61",
"source": {
"type": "git",
"url": "https://github.com/hyperf/config-nacos.git",
"reference": "ad46dffbc252153aebef0fc0918ec85d8335bfc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hyperf/config-nacos/zipball/ad46dffbc252153aebef0fc0918ec85d8335bfc9",
"reference": "ad46dffbc252153aebef0fc0918ec85d8335bfc9",
"shasum": ""
},
"require": {
"hyperf/codec": "~3.1.0",
"hyperf/collection": "~3.1.0",
"hyperf/config-center": "~3.1.0",
"hyperf/contract": "~3.1.0",
"hyperf/guzzle": "~3.1.0",
"hyperf/nacos": "~3.1.0",
"hyperf/support": "~3.1.0",
"jetbrains/phpstorm-attributes": "^1.0",
"php": ">=8.1"
},
"suggest": {
"ext-json": "*",
"ext-simplexml": "*",
"ext-yaml": "*",
"hyperf/event": "Required to use listeners. (~2.2.0)",
"hyperf/framework": "Required to use listeners. (~2.2.0)",
"hyperf/process": "Required to use processes. (~2.2.0)"
},
"type": "library",
"extra": {
"hyperf": {
"config": "Hyperf\\ConfigNacos\\ConfigProvider"
},
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Hyperf\\ConfigNacos\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A nacos adapter for config center component.",
"homepage": "https://hyperf.io",
"keywords": [
"hyperf",
"nacos",
"php",
"swoole"
],
"support": {
"docs": "https://hyperf.wiki",
"issues": "https://github.com/hyperf/hyperf/issues",
"pull-request": "https://github.com/hyperf/hyperf/pulls",
"source": "https://github.com/hyperf/hyperf"
},
"funding": [
{
"url": "https://hyperf.wiki/#/zh-cn/donate",
"type": "custom"
},
{
"url": "https://opencollective.com/hyperf",
"type": "open_collective"
}
],
"time": "2025-09-03T05:52:19+00:00"
},
{ {
"name": "hyperf/context", "name": "hyperf/context",
"version": "v3.1.42", "version": "v3.1.42",
@@ -4181,6 +4325,72 @@
], ],
"time": "2025-06-06T02:41:30+00:00" "time": "2025-06-06T02:41:30+00:00"
}, },
{
"name": "hyperf/utils",
"version": "v3.1.42",
"source": {
"type": "git",
"url": "https://github.com/hyperf/utils.git",
"reference": "4b13a567a61d08a3c4d058499e28a5b26fc18f1c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hyperf/utils/zipball/4b13a567a61d08a3c4d058499e28a5b26fc18f1c",
"reference": "4b13a567a61d08a3c4d058499e28a5b26fc18f1c",
"shasum": ""
},
"require": {
"doctrine/inflector": "^2.0",
"hyperf/code-parser": "~3.1.0",
"hyperf/codec": "~3.1.0",
"hyperf/collection": "~3.1.0",
"hyperf/context": "~3.1.0",
"hyperf/contract": "~3.1.0",
"hyperf/coordinator": "~3.1.0",
"hyperf/coroutine": "~3.1.0",
"hyperf/engine": "^2.0",
"hyperf/macroable": "~3.1.0",
"hyperf/serializer": "~3.1.0",
"hyperf/stringable": "~3.1.0",
"hyperf/support": "~3.1.0",
"php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A tools package that could help developer solved the problem quickly.",
"homepage": "https://hyperf.io",
"keywords": [
"hyperf",
"php",
"swoole",
"utils"
],
"support": {
"docs": "https://hyperf.wiki",
"issues": "https://github.com/hyperf/hyperf/issues",
"pull-request": "https://github.com/hyperf/hyperf/pulls",
"source": "https://github.com/hyperf/hyperf"
},
"funding": [
{
"url": "https://hyperf.wiki/#/zh-cn/donate",
"type": "custom"
},
{
"url": "https://opencollective.com/hyperf",
"type": "open_collective"
}
],
"time": "2024-09-25T02:54:12+00:00"
},
{ {
"name": "hyperf/validation", "name": "hyperf/validation",
"version": "v3.1.60", "version": "v3.1.60",

16
config/autoload/nacos.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
use function Hyperf\Support\env;
return [
'default' => [
'host' => env('NACOS_HOST', '127.0.0.1'),
'port' => env('NACOS_PORT', 8848),
'username' => env('NACOS_USERNAME', 'nacos'),
'password' => env('NACOS_PASSWORD', 'nacos'),
'namespace' => env('NACOS_NAMESPACE', 'public'),
'timeout' => 5.0,
],
];

View File

@@ -37,7 +37,7 @@ return [
], ],
'settings' => [ 'settings' => [
Constant::OPTION_ENABLE_COROUTINE => true, Constant::OPTION_ENABLE_COROUTINE => true,
Constant::OPTION_WORKER_NUM => (int) env('SWOOLE_WORKER_NUM', swoole_cpu_num()), //swoole_cpu_num(), Constant::OPTION_WORKER_NUM => (int) env('SWOOLE_WORKER_NUM', swoole_cpu_num()), // swoole_cpu_num(),
Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid', Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid',
Constant::OPTION_OPEN_TCP_NODELAY => true, Constant::OPTION_OPEN_TCP_NODELAY => true,
Constant::OPTION_MAX_COROUTINE => 100000, Constant::OPTION_MAX_COROUTINE => 100000,
@@ -51,4 +51,10 @@ return [
Event::ON_PIPE_MESSAGE => [PipeMessageCallback::class, 'onPipeMessage'], Event::ON_PIPE_MESSAGE => [PipeMessageCallback::class, 'onPipeMessage'],
Event::ON_WORKER_EXIT => [WorkerExitCallback::class, 'onWorkerExit'], Event::ON_WORKER_EXIT => [WorkerExitCallback::class, 'onWorkerExit'],
], ],
// 健康检查配置供Nacos检测服务状态
'health_check' => [
'enable' => true,
'path' => '/health',
'interval' => 3,
],
]; ];

View File

@@ -1,30 +1,30 @@
<?php <?php
declare(strict_types=1);
use function Hyperf\Support\env; use function Hyperf\Support\env;
return [ return [
'enable' => [ 'enable' => true,
'discovery' => true, 'driver' => 'nacos',
'register' => true, 'registries' => [
],
'consumers' => [],
'providers' => [],
'drivers' => [
'nacos' => [ 'nacos' => [
// nacos server url like https://nacos.hyperf.io, Priority is higher than host:port
// 'url' => '',
// The nacos host info
'host' => env('NACOS_HOST', '127.0.0.1'), 'host' => env('NACOS_HOST', '127.0.0.1'),
'port' => env('NACOS_PORT', 8848), 'port' => env('NACOS_PORT', 8848),
// The nacos account info 'username' => env('NACOS_USERNAME', 'nacos'),
'username' => null, 'password' => env('NACOS_PASSWORD', 'nacos'),
'password' => null, 'namespace' => env('NACOS_NAMESPACE', 'public'),
'guzzle' => [ 'group_name' => env('NACOS_GROUP_NAME', 'DEFAULT_GROUP'),
'config' => null, 'service_name' => env('NACOS_SERVICE_NAME', 'hyperf-organization'),
'protect_threshold' => 0.5,
'metadata' => [
'protocol' => 'jsonrpc-http',
'server' => 'jsonrpc-http',
'version' => '1.0.0',
'weight' => 100,
], ],
'group_name' => 'api',
'namespace_id' => env('NACOS_NAMESPACE', 'dev'),
'heartbeat' => 5, 'heartbeat' => 5,
'heartbeat_timeout' => 15,
], ],
], ],
]; ];

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Aether\Traits; namespace Aether\Traits;
use App\Notice\Enum\NoticeStatusEnum; use App\Notice\Enum\NoticeStatusEnum;
use App\Notice\Model\NoticeStatsModel; use App\Data\Model\NoticeStatsModel;
use InvalidArgumentException; use InvalidArgumentException;
use ReflectionClass; use ReflectionClass;