核心库补齐Aether兼容方法与树形Trait,中间件复用RPC上下文

This commit is contained in:
堂吉诃德拉曼查的英豪
2026-08-06 09:26:01 +08:00
parent 8ca756d0ee
commit dd2b32cc95
3 changed files with 228 additions and 12 deletions

View File

@@ -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);

View File

@@ -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
View 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);
}
}
}
}