shouldCache($request)) { return $next($request); } $key = $this->key($request); $ttl = (int) config('blog.page_cache_ttl', 300); if ($cached = Cache::get($key)) { self::increment('hits'); return response($cached['body'], 200, [ 'Content-Type' => $cached['type'], 'Cache-Control' => 'public, max-age='.$ttl, ]); } self::increment('misses'); $response = $next($request); if ($response->getStatusCode() === 200) { Cache::put($key, [ 'body' => $response->getContent(), 'type' => $response->headers->get('Content-Type') ?: 'text/html; charset=utf-8', ], $ttl); self::rememberKey($key); } return $response; } public static function flush(): void { foreach ((array) Cache::get(self::TAG.'_keys', []) as $key) { Cache::forget($key); } Cache::forget(self::TAG.'_keys'); } public static function count(): int { return count((array) Cache::get(self::TAG.'_keys', [])); } /** * 命中/未命中统计(按日滚动),用于判断前台缓存效果。 */ public static function stats(): array { $hits = (int) Cache::get(self::TAG.'_hits', 0); $misses = (int) Cache::get(self::TAG.'_misses', 0); $total = $hits + $misses; return [ 'hits' => $hits, 'misses' => $misses, 'count' => self::count(), 'rate' => $total > 0 ? round($hits / $total * 100, 1) : 0.0, ]; } public static function resetStats(): void { Cache::forget(self::TAG.'_hits'); Cache::forget(self::TAG.'_misses'); } private static function increment(string $metric): void { $key = self::TAG.'_'.$metric; Cache::put($key, (int) Cache::get($key, 0) + 1, now()->addDay()); } private static function rememberKey(string $key): void { $keys = (array) Cache::get(self::TAG.'_keys', []); $keys[$key] = true; Cache::put(self::TAG.'_keys', $keys, now()->addDay()); } private function shouldCache(Request $request): bool { if (! $request->isMethod('GET')) { return false; } if (auth()->check()) { return false; } $path = $request->path(); if (str_starts_with($path, 'admin') || str_starts_with($path, 'api') || str_starts_with($path, 'themes')) { return false; } // 动态端点 if (str_starts_with($path, 'track-view')) { return false; } return true; } private function key(Request $request): string { return self::TAG.':'.md5($request->fullUrl()); } }