perf: V0.9.4 商品列表分页 + sitemap 文件缓存 + banner/asset/gzip/类型修复

- 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+ 弃用)
This commit is contained in:
2026-08-08 23:53:52 +08:00
parent 2285998618
commit d5c1edefae
8 changed files with 83 additions and 7 deletions
+28
View File
@@ -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'] ?? '';