getQueryString()); $payload = Cache::remember("tools_index_{$querySignature}", now()->addMinutes(10), function () use ($request): array { $builder = Tool::query() ->published() ->with(['category', 'alternative']); $this->applyFilters($builder, $request); return [ 'items' => $builder ->orderByDesc('published_at') ->paginate(20) ->withQueryString(), 'categories' => Category::query()->where('type', 'tool')->where('is_active', true)->orderBy('name')->get(), 'filters' => $request->only(['q', 'category', 'pricing', 'api']), ]; }); return view('public.tools.index', $payload); } public function show(string $slug): View { /** @var Tool $tool */ $tool = Tool::query() ->published() ->with(['category', 'source', 'alternative']) ->where('slug', $slug) ->firstOrFail(); $relatedTools = Tool::query() ->published() ->whereKeyNot($tool->id) ->when($tool->category_id !== null, fn (Builder $query): Builder => $query->where('category_id', $tool->category_id)) ->orderByDesc('published_at') ->limit(6) ->get(); return view('public.tools.show', [ 'item' => $tool, 'relatedTools' => $relatedTools, 'showRiskNotice' => $this->containsRiskKeyword($tool->summary.' '.$tool->description), ]); } private function applyFilters(Builder $builder, Request $request): void { if ($request->filled('q')) { $keyword = trim((string) $request->string('q')); $builder->whereFullText(['name', 'summary', 'description'], $keyword); } if ($request->filled('category')) { $builder->whereHas('category', function (Builder $categoryQuery) use ($request): void { $categoryQuery->where('slug', (string) $request->string('category')); }); } if ($request->filled('pricing')) { $builder->where('pricing_type', (string) $request->string('pricing')); } if ($request->filled('api')) { $builder->where('has_api', $request->boolean('api')); } } private function containsRiskKeyword(string $text): bool { return str_contains($text, '医疗') || str_contains($text, '法律') || str_contains($text, '投资'); } }