核心库补齐Aether兼容方法与树形Trait,中间件复用RPC上下文
This commit is contained in:
@@ -22,13 +22,15 @@ class RequestContextMiddleware implements MiddlewareInterface
|
||||
{
|
||||
$rpcContext = Util::get(RpcContext::class);
|
||||
|
||||
$traceId = $request->getHeaderLine('X-Trace-Id')
|
||||
?: uniqid('t-', true);
|
||||
$traceId = $rpcContext->get('trace_id');
|
||||
if (empty($traceId)) {
|
||||
$traceId = $request->getHeaderLine('X-Trace-Id') ?: uniqid('t-', true);
|
||||
}
|
||||
Context::set('trace_id', $traceId);
|
||||
$rpcContext->set('trace_id', $traceId);
|
||||
|
||||
// app版本
|
||||
$appVersion = $request->getHeaderLine('X-App-Version') ?: '';
|
||||
$appVersion = $rpcContext->get('app_version') ?: $request->getHeaderLine('X-App-Version') ?: '';
|
||||
Context::set('app_version', $appVersion);
|
||||
$rpcContext->set('app_version', $appVersion);
|
||||
|
||||
@@ -36,7 +38,7 @@ class RequestContextMiddleware implements MiddlewareInterface
|
||||
// APP: app
|
||||
// 微信小程序: wx_mini
|
||||
// 官网: web_official
|
||||
$platformName = $request->getHeaderLine('X-Platform-Name') ?: '';
|
||||
$platformName = $rpcContext->get('platform_name') ?: $request->getHeaderLine('X-Platform-Name') ?: '';
|
||||
|
||||
Context::set('platform_name', $platformName);
|
||||
$rpcContext->set('platform_name', $platformName);
|
||||
|
||||
100
src/Model.php
100
src/Model.php
@@ -119,13 +119,7 @@ abstract class Model extends HyperfModel implements CacheableInterface
|
||||
}
|
||||
|
||||
$this->applySearch($query, $params);
|
||||
|
||||
if ($this->sortable) {
|
||||
$sortConfig = $this->getSortConfig();
|
||||
if ($sortConfig) {
|
||||
$query->orderBy($sortConfig['field'], $sortConfig['direction']);
|
||||
}
|
||||
}
|
||||
$this->applyOrdering($query, $params);
|
||||
|
||||
$withDeleted = filter_var($params['with_deleted'] ?? false, FILTER_VALIDATE_BOOLEAN);
|
||||
if ($withDeleted) {
|
||||
@@ -208,12 +202,48 @@ abstract class Model extends HyperfModel implements CacheableInterface
|
||||
$searchParams = array_intersect_key($params, array_flip($allowedSearchFields));
|
||||
|
||||
// 合并系统参数与合法搜索参数(排除其他未知参数)
|
||||
$systemParams = ['page', 'size', 'with_deleted', 'parent_id'];
|
||||
$systemParams = ['page', 'size', 'with_deleted', 'parent_id', 'order_by', 'order_direction'];
|
||||
$validSystemParams = array_intersect_key($params, array_flip($systemParams));
|
||||
|
||||
return array_merge($validSystemParams, $searchParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用排序逻辑,支持自定义 order_by/order_direction.
|
||||
*/
|
||||
protected function applyOrdering(Builder $query, array $params): void
|
||||
{
|
||||
$orderField = $params['order_by'] ?? null;
|
||||
$orderDirection = strtolower((string) ($params['order_direction'] ?? 'asc'));
|
||||
|
||||
if (is_string($orderField) && $this->isValidOrderField($orderField)) {
|
||||
$direction = $orderDirection === 'desc' ? 'desc' : 'asc';
|
||||
$query->orderBy($orderField, $direction);
|
||||
if ($orderField !== 'id' && ! str_contains($orderField, '.')) {
|
||||
$query->orderBy('id', 'asc');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->sortable) {
|
||||
$sortConfig = $this->getSortConfig();
|
||||
if ($sortConfig) {
|
||||
$query->orderBy($sortConfig['field'], $sortConfig['direction']);
|
||||
if ($sortConfig['field'] !== 'id' && ! str_contains($sortConfig['field'], '.')) {
|
||||
$query->orderBy('id', 'asc');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验排序字段,避免 SQL 注入.
|
||||
*/
|
||||
protected function isValidOrderField(string $field): bool
|
||||
{
|
||||
return (bool) preg_match('/^[a-zA-Z0-9_\.]+$/', $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷创建.
|
||||
*/
|
||||
@@ -290,6 +320,60 @@ abstract class Model extends HyperfModel implements CacheableInterface
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找记录,不存在时抛出业务异常.
|
||||
*/
|
||||
public static function findOrThrow($id, string $message = '数据不存在'): static
|
||||
{
|
||||
$record = static::query()->find($id);
|
||||
if (is_null($record)) {
|
||||
throw new BusinessException($message, 404);
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aether 兼容:创建记录并返回 ID.
|
||||
*/
|
||||
public static function createBy(array $data): int
|
||||
{
|
||||
return static::query()->create($data)->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aether 兼容:按主键查找,不存在时抛出业务异常.
|
||||
*/
|
||||
public static function findByPk(int $id): array|Builder|Collection|HyperfModel
|
||||
{
|
||||
$record = static::query()->find($id);
|
||||
if (is_null($record)) {
|
||||
throw new BusinessException(sprintf('找不到 ID 为 %d 的记录', $id), 404);
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aether 兼容:按主键更新.
|
||||
*/
|
||||
public static function updateByPk(int $id, array $data): bool
|
||||
{
|
||||
if (! static::query()->where('id', $id)->exists()) {
|
||||
throw new BusinessException(sprintf('找不到 ID 为 %d 的记录', $id), 404);
|
||||
}
|
||||
return static::query()->where('id', $id)->update($data) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aether 兼容:按主键删除.
|
||||
*/
|
||||
public static function deleteByPk(int $id): bool
|
||||
{
|
||||
if (! static::query()->where('id', $id)->exists()) {
|
||||
throw new BusinessException(sprintf('找不到 ID 为 %d 的记录', $id), 404);
|
||||
}
|
||||
return static::query()->where('id', $id)->delete() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取器处理.
|
||||
*/
|
||||
|
||||
130
src/Trait/TreeTrait.php
Normal file
130
src/Trait/TreeTrait.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dhd\Micro\Core\Trait;
|
||||
|
||||
use Dhd\Micro\Core\Model;
|
||||
use Hyperf\Database\Model\Collection;
|
||||
use LogicException;
|
||||
|
||||
trait TreeTrait
|
||||
{
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
parent::__construct($attributes);
|
||||
|
||||
if (! $this instanceof Model) {
|
||||
throw new LogicException(
|
||||
'使用TreeTrait的类必须继承Dhd\Micro\Core\Model,当前类: ' . get_class($this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建树形结构.
|
||||
*/
|
||||
public static function buildTree($items, int $parentId = 0): array
|
||||
{
|
||||
$self = new static();
|
||||
$parentField = $self->getParentIdField();
|
||||
$sortField = $self->getSortField();
|
||||
|
||||
$items = $items instanceof Collection ? $items->toArray() : $items;
|
||||
$tree = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item[$parentField] == $parentId) {
|
||||
$children = static::buildTree($items, $item['id']);
|
||||
if (! empty($children)) {
|
||||
$item['children'] = $children;
|
||||
}
|
||||
$tree[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$self->sortTreeItems($tree, $sortField);
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定节点的所有子节点ID.
|
||||
*/
|
||||
public function getChildIds(int $id): array
|
||||
{
|
||||
$parentField = $this->getParentIdField();
|
||||
$allItems = $this->newQuery()->get(['id', $parentField])->toArray();
|
||||
$ids = [$id];
|
||||
|
||||
$this->collectChildIds($allItems, $id, $parentField, $ids);
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点的完整路径.
|
||||
*/
|
||||
public function getPath(int $id): array
|
||||
{
|
||||
$parentField = $this->getParentIdField();
|
||||
$node = $this->newQuery()->find($id);
|
||||
if (! $node) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$path = [$node->toArray()];
|
||||
$parentId = $node[$parentField];
|
||||
|
||||
while ($parentId > 0) {
|
||||
$parent = $this->newQuery()->find($parentId);
|
||||
if (! $parent) {
|
||||
break;
|
||||
}
|
||||
array_unshift($path, $parent->toArray());
|
||||
$parentId = $parent[$parentField];
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父ID字段名(子类实现).
|
||||
*/
|
||||
abstract protected function getParentIdField(): string;
|
||||
|
||||
/**
|
||||
* 获取排序字段名(子类实现).
|
||||
*/
|
||||
abstract protected function getSortField(): string;
|
||||
|
||||
/**
|
||||
* 树形节点排序.
|
||||
*/
|
||||
protected function sortTreeItems(array &$items, string $sortField): void
|
||||
{
|
||||
if (empty($sortField)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usort($items, function ($a, $b) use ($sortField) {
|
||||
$direction = $this->treeSortDirection ?? 'asc';
|
||||
$aValue = $a[$sortField] ?? 0;
|
||||
$bValue = $b[$sortField] ?? 0;
|
||||
return $direction === 'desc'
|
||||
? $bValue <=> $aValue
|
||||
: $aValue <=> $bValue;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归收集子节点ID.
|
||||
*/
|
||||
private function collectChildIds(array $items, int $parentId, string $parentField, array &$ids): void
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
if ($item[$parentField] == $parentId) {
|
||||
$ids[] = $item['id'];
|
||||
$this->collectChildIds($items, $item['id'], $parentField, $ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user