logger = $this->loggerFactory->get($this->getLoggerName()); } /** * 通用列表查询(支持分页和树形结构). */ public function list(array $params = []): array { $model = $this->getModel(); $query = $model->newQuery(); // 通过模型配置自动应用所有搜索条件 $this->applySearch($query, $params); // 动态应用排序 $sortConfig = $model->getSortConfig(); if ($sortConfig) { $query->orderBy($sortConfig['field'], $sortConfig['direction']); } $withDeleted = filter_var($params['with_deleted'] ?? false, FILTER_VALIDATE_BOOLEAN); if ($withDeleted) { $query->withTrashed(); } // 存在分页参数(page或size)则进行分页查询 if (isset($params['page']) || isset($params['size'])) { $page = (int) ($params['page'] ?? 1); $size = (int) ($params['size'] ?? 10); // 确保分页参数合法性 $page = max(1, $page); $size = max(1, min(100, $size)); // 限制最大页大小为100 $result = $query->paginate($size, ['*'], 'page', $page); return [ 'total' => $result->total(), 'list' => $result->items(), ]; } // 无分页参数时返回完整数据集合 $items = $query->get()->toArray(); // 若模型支持树形结构则构建树形,否则返回普通数组 if ($model instanceof TreeableInterface) { return $model::buildTree($items, (int) ($params['parent_id'] ?? 0)); } return $items; } /** * 通用详情查询. */ public function detail(int $id): object { $this->logger->info('获取资源详情', ['id' => $id]); return $this->getModel()->findOrFailById($id); } /** * 通用创建逻辑. * @throws BusinessException|Throwable */ public function create(array $data): int { // 数据验证(使用子类指定的验证器) $this->getValidator()->scene('create', $data)->check(); return $this->transaction(function () use ($data) { $model = $this->getModel()->createOne($data); $this->logger()->info('创建资源', [ 'id' => $model->id, 'code' => $data['code'] ?? $model->code, ]); return $model->id; }); } /** * 通用更新逻辑. * @throws BusinessException|Throwable */ public function update(int $id, array $data): bool { $model = $this->getModel(); $resource = $model->findById($id); $this->checkResourceExists($resource); // 数据验证 $this->getValidator()->scene('update', $data)->check(); // 钩子:处理更新时的特殊逻辑(如禁止自身为父级) $this->handleUpdateSpecialLogic($id, $data); return $this->transaction(function () use ($id, $data) { $result = $this->getModel()->updateById($id, $data); $this->logger()->info('更新资源', [ 'id' => $id, 'data' => $data, ]); return $result; }); } /** * 通用删除逻辑. * @throws BusinessException|Throwable */ public function delete(int $id): bool { $model = $this->getModel(); $resource = $model->findById($id); $this->checkResourceExists($resource); // 钩子:删除前检查(如子级存在性) $this->checkChildrenBeforeDelete($id); return $this->transaction(function () use ($id) { $result = $this->getModel()->deleteById($id); $this->logger()->info('删除资源', ['id' => $id]); return $result; }); } /** * 根据模型的$search配置,自动应用搜索条件到查询构建器. */ public function applySearch(Builder $query, array $params): void { foreach ($this->search as $field => $rule) { // 跳过未传递的参数 if (! isset($params[$field])) { continue; } $value = $params[$field]; $this->applySearchRule($query, $field, $value, $rule); } } /** * 软删除恢复. * @throws BusinessException|Throwable */ public function restore(int $id): bool { $model = $this->getModel(); // 必须使用withTrashed()才能查询到已删除记录 $resource = $model->newQuery()->withTrashed()->find($id); $this->checkResourceExists($resource, '恢复的资源不存在'); return $this->transaction(function () use ($id) { $result = $this->getModel()->newQuery()->withTrashed() ->where('id', $id)->restore(); $this->logger()->info('恢复软删除资源', ['id' => $id]); return $result; }); } /** * 批量软删除. * @throws BusinessException|Throwable */ public function batchDelete(array $ids): bool { if (empty($ids)) { throw new BusinessException('请选择要删除的记录', 400); } $model = $this->getModel(); $exists = $model->whereIn('id', $ids)->exists(); if (! $exists) { throw new BusinessException('部分记录不存在', 404); } return $this->transaction(function () use ($ids) { $result = $this->getModel()->whereIn('id', $ids)->delete(); $this->logger()->info('批量软删除资源', ['ids' => $ids]); return $result > 0; }); } /** * 批量恢复软删除. * @throws BusinessException|Throwable */ public function batchRestore(array $ids): bool { if (empty($ids)) { throw new BusinessException('请选择要恢复的记录', 400); } $model = $this->getModel(); $exists = $model->newQuery()->withTrashed() ->whereIn('id', $ids) ->whereNotNull('deleted_at') ->exists(); if (! $exists) { throw new BusinessException('部分记录不存在或未被删除', 404); } return $this->transaction(function () use ($ids) { $result = $this->getModel()->newQuery()->withTrashed() ->whereIn('id', $ids)->restore(); $this->logger()->info('批量恢复资源', ['ids' => $ids]); return $result > 0; }); } /** * 永久删除(物理删除). * @throws BusinessException|Throwable */ public function forceDelete(int $id): bool { $model = $this->getModel(); $resource = $model->newQuery()->withTrashed()->find($id); $this->checkResourceExists($resource, '要删除的资源不存在'); return $this->transaction(function () use ($id) { $result = $this->getModel()->newQuery()->withTrashed() ->where('id', $id)->forceDelete(); $this->logger()->info('永久删除资源', ['id' => $id]); return $result; }); } /** * 获取日志名称. */ protected function getLoggerName(): string { return strtolower((new ReflectionClass($this))->getShortName()); } protected function getSearch(): array { return $this->search; } protected function getIgnoreSearchFields(): array { return $this->ignoreSearchFields; } /** * 获取当前服务对应的模型实例(由子类实现). */ abstract protected function getModel(): AetherModel; /** * 获取当前服务对应的验证器实例(由子类实现). */ abstract protected function getValidator(): AetherValidator; /** * 钩子方法:更新时的特殊逻辑(子类可重写). */ protected function handleUpdateSpecialLogic(int $id, array &$data): void { // 通用逻辑:禁止将自身设为父级(适用于有parent_id的场景) if (isset($data['parent_id']) && $data['parent_id'] == $id) { throw new BusinessException('不能将自身设为父级', 400); } } /** * 钩子方法:删除前检查子级(子类可重写). */ protected function checkChildrenBeforeDelete(int $id): void { // 默认不检查,需要的子类重写 } /** * 应用单个搜索规则. * @param mixed $value * @param mixed $rule */ protected function applySearchRule(Builder $query, string $field, $value, $rule): void { // 处理规则格式(支持字符串简写或数组配置) $config = is_array($rule) ? $rule : ['type' => $rule]; $type = $config['type']; switch ($type) { case '=': // 精确匹配 $query->where($field, $value); break; case 'like': // 模糊匹配 $query->where($field, 'like', "%{$value}%"); break; case 'between': // 范围查询(支持数组或两个参数) $values = is_array($value) ? $value : [$value, $params[$field . '_end'] ?? $value]; $query->whereBetween($field, $values); break; case 'callback': // 自定义回调 if (isset($config['handler']) && is_callable($config['handler'])) { call_user_func($config['handler'], $query, $value); } break; // 可扩展其他类型:in、>、< 等 } } }