From d5c1edefae3eeb2ed2d78766f644002c90ca2270 Mon Sep 17 00:00:00 2001 From: lucanlee Date: Sat, 8 Aug 2026 23:53:52 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20V0.9.4=20=E5=95=86=E5=93=81=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=88=86=E9=A1=B5=20+=20sitemap=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=BC=93=E5=AD=98=20+=20banner/asset/gzip/=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - P1: Product::listing() 分页(WHERE status=1+LIMIT/OFFSET+总数),ProductController 改用并加翻页器,消除列表全表 - P1: outputSitemap() 加 storage/cache/sitemap.xml 文件缓存(TTL 3600s),消除每次爬虫全表 - P2: 首页 banner 改 whereLimit('status',1,20) - P2: asset() 追加 ?v=filemtime 版本串,theme.css 改后自动击穿 1y 浏览器缓存 - P2: nginx 启用 gzip(文本资源压缩) - P2: Model::count() $val 隐式可空改 mixed(PHP 8.4+ 弃用) --- app/Controllers/HomeController.php | 2 +- app/Controllers/ProductController.php | 12 +++++++++--- app/Core/App.php | 16 ++++++++++++++- app/Core/Helper.php | 9 ++++++++- app/Core/Model.php | 2 +- app/Models/Product.php | 28 +++++++++++++++++++++++++++ app/Views/product/index.php | 13 +++++++++++++ coolcoth.com.conf | 8 ++++++++ 8 files changed, 83 insertions(+), 7 deletions(-) diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php index 4aad315..6a673d2 100644 --- a/app/Controllers/HomeController.php +++ b/app/Controllers/HomeController.php @@ -52,7 +52,7 @@ class HomeController extends Controller 'noindex' => $seo['noindex'], 'jsonld' => $faqJsonLd, ], - 'banners' => array_filter($banner->all(), fn($b) => ($b['status'] ?? 1) == 1), + 'banners' => $banner->whereLimit('status', 1, 20), 'categories'=> $category->all(), 'products' => $product->featured(8), 'news' => $news->published(3), diff --git a/app/Controllers/ProductController.php b/app/Controllers/ProductController.php index 8dd5f3a..2a5c8d3 100644 --- a/app/Controllers/ProductController.php +++ b/app/Controllers/ProductController.php @@ -10,11 +10,15 @@ class ProductController extends Controller { $product = new Product(); $category = new Category(); + $perPage = 12; + $page = max(1, (int)($_GET['page'] ?? 1)); $catId = isset($_GET['cat']) ? (int)$_GET['cat'] : 0; - $products = $catId - ? $product->byCategory($catId) - : array_filter($product->all(), fn($p) => ($p['status'] ?? 1) == 1); + // V0.9.4:列表改为分页查询(WHERE status=1 + LIMIT/OFFSET + 总数),不再全表拉回。 + $res = $product->listing($page, $perPage, $catId ?: null); + $products = $res['items']; + $total = $res['total']; + $totalPages = max(1, (int) ceil($total / $perPage)); $cat = $catId ? $category->find($catId) : null; $catName = $cat['name'] ?? ''; @@ -56,6 +60,8 @@ class ProductController extends Controller 'categories'=> $category->all(), 'activeCat' => $catId, 'cat' => $cat, + 'page' => $page, + 'totalPages'=> $totalPages, ]); } diff --git a/app/Core/App.php b/app/Core/App.php index d15f791..8a4fb0e 100644 --- a/app/Core/App.php +++ b/app/Core/App.php @@ -328,9 +328,17 @@ class App return $v; } - /** 动态生成 Sitemap XML(Google/Bing/Baidu 自动抓取) */ + /** 动态生成 Sitemap XML(Google/Bing/Baidu 自动抓取)。V0.9.4:加文件缓存(TTL),避免每次爬虫请求全表扫描。 */ private static function outputSitemap(): void { + $cacheFile = rtrim(BASE_PATH, '/') . '/storage/cache/sitemap.xml'; + $ttl = 3600; + if (is_file($cacheFile) && (time() - filemtime($cacheFile)) < $ttl) { + header('Content-Type: application/xml; charset=utf-8'); + readfile($cacheFile); + exit; + } + ob_start(); header('Content-Type: application/xml; charset=utf-8'); echo '' . "\n"; echo '' . "\n"; @@ -402,6 +410,12 @@ class App } echo ''; + $xml = ob_get_clean(); + // 写文件缓存(best-effort:目录不存在则创建,写失败不影响本次输出) + $dir = dirname($cacheFile); + if (!is_dir($dir)) @mkdir($dir, 0755, true); + if (is_dir($dir)) @file_put_contents($cacheFile, $xml); + echo $xml; exit; } } diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 9e2437d..e6ec1ff 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -24,7 +24,14 @@ if (!function_exists('site_url')) { } function asset(string $path = ''): string { - return site_url('assets/' . ltrim($path, '/')); + $url = site_url('assets/' . ltrim($path, '/')); + // V0.9.4:追加文件 mtime 作为版本串,使 theme.css 等静态资源在「重新生成/部署」后自动击穿浏览器长缓存 + //(nginx 对已改资源设了 expires 1y,无版本串会导致老访客最长 1 年看不到更新)。 + $real = rtrim(BASE_PATH, '/') . '/public/assets/' . ltrim($path, '/'); + if (is_file($real)) { + $url .= '?v=' . filemtime($real); + } + return $url; } function e($v): string { diff --git a/app/Core/Model.php b/app/Core/Model.php index 95ff34f..2c1b248 100644 --- a/app/Core/Model.php +++ b/app/Core/Model.php @@ -193,7 +193,7 @@ class Model * @param string|null $col 过滤列(null 表示全表计数) * @param mixed $val 过滤值 */ - public function count(?string $col = null, $val = null): int + public function count(?string $col = null, mixed $val = null): int { if (Db::driver() === 'mysql') { if ($col === null) { diff --git a/app/Models/Product.php b/app/Models/Product.php index 0dc34d1..dd80f14 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -18,6 +18,34 @@ class Product extends Model // 不再 SELECT * 全表拉回再 array_filter(原写法每次请求都全表扫描)。 return $this->whereLimit('status', 1, $limit); } + /** + * V0.9.4:产品列表分页。仅取 status=1 的活跃商品,按 $orderBy 排序,带 LIMIT/OFFSET; + * 同时返回总数用于翻页。避免每次列表请求都 SELECT * 全表拉回再 array_filter(原写法随 SKU 增长变重)。 + * 文件模式回退为全表过滤 + array_slice。 + * @return array{items:array,total:int} + */ + public function listing(int $page, int $perPage, ?int $catId = null): array + { + $page = max(1, $page); + $perPage = max(1, $perPage); + $offset = ($page - 1) * $perPage; + if (\Core\Db::driver() === 'mysql') { + $where = 'WHERE status = 1'; + $args = []; + if ($catId !== null) { $where .= ' AND category_id = ?'; $args[] = $catId; } + $items = \Core\Db::query( + "SELECT * FROM `{$this->table}` {$where} ORDER BY `{$this->orderBy}` ASC LIMIT ? OFFSET ?", + array_merge($args, [$perPage, $offset]) + )->fetchAll(); + $total = (int) \Core\Db::query("SELECT COUNT(*) FROM `{$this->table}` {$where}", $args)->fetchColumn(); + return ['items' => $items, 'total' => $total]; + } + // 文件模式兜底 + $all = array_filter($this->all(), fn($p) => ($p['status'] ?? 1) == 1); + if ($catId !== null) $all = array_filter($all, fn($p) => ($p['category_id'] ?? 0) == $catId); + $all = array_values($all); + return ['items' => array_slice($all, $offset, $perPage), 'total' => count($all)]; + } public function specsArray($p): array { $s = $p['specs'] ?? ''; diff --git a/app/Views/product/index.php b/app/Views/product/index.php index 971a1cb..56d35fb 100644 --- a/app/Views/product/index.php +++ b/app/Views/product/index.php @@ -44,5 +44,18 @@ $sub = $cat ? $cat['description'] : '水冷循环 / 相变蓄冷 / 涡扇风冷 + 1): ?> + site_url($base . ($activeCat ? '&' : '?') . 'page=' . $p); + ?> + + diff --git a/coolcoth.com.conf b/coolcoth.com.conf index e238218..a376b69 100644 --- a/coolcoth.com.conf +++ b/coolcoth.com.conf @@ -24,6 +24,14 @@ server { root /www/wwwroot/coolcoth.com/public; # 运行目录 = /public index index.php index.html; + # ── Gzip 压缩(V0.9.4:文本资源压缩,降低传输体积)── + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 5; + gzip_types text/plain text/css text/xml application/xml application/javascript application/json application/x-javascript image/svg+xml; + # ── SSL 证书(宝塔申请 Let's Encrypt 后自动填充,或手动指定)── # ssl_certificate /www/server/panel/vhost/cert/coolcoth.com/fullchain.pem; # ssl_certificate_key /www/server/panel/vhost/cert/coolcoth.com/privkey.pem;