feat:實作 API 效能優化 — Redis 快取、分頁、DB 索引
Tests / PHP 8.2 (push) Failing after 1m35s
Tests / PHP 8.3 (push) Failing after 2s

- 引入 Redis(predis)快取層:Admin Stats(5分鐘)、課程列表(3分鐘,tag-based 失效)、評價分布(10分鐘)
- ReviewController::publicList 改為 paginate + eager load votes,消除 N+1
- AdminReviewController::index 加入分頁(預設 20,最大 100)
- 新增 notifications / diving_offers 效能索引 migration
- 新增 docker-compose.override.yml 本機開發 port mapping 機制(不進 git)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 00:17:16 +08:00
parent 6877ef30b5
commit 0455c1577e
18 changed files with 516 additions and 93 deletions
@@ -0,0 +1,31 @@
## ADDED Requirements
### Requirement: Notifications 表補複合索引
`notifications` 表 SHALL 新增 `[notifiable_type, notifiable_id, read_at]` 複合索引,以加速 `unreadNotifications()` 查詢。
#### Scenario: Migration 執行成功
- **WHEN** 執行 `php artisan migrate`
- **THEN** `notifications` 表上存在 `notifications_notifiable_read_at_index`(或同等命名的)複合索引,`EXPLAIN` 結果不再為 full table scan
#### Scenario: 未讀通知查詢走索引
- **WHEN** 系統執行 `Notification::where('notifiable_type', User::class)->where('notifiable_id', $userId)->whereNull('read_at')->get()`
- **THEN** MySQL `EXPLAIN` 顯示使用複合索引,`type``ref` 而非 `ALL`
---
### Requirement: DivingOffers 表補 provider_id 索引
`diving_offers` 表 SHALL 新增 `provider_id` 單欄索引,以加速 Provider 課程列表查詢。
#### Scenario: Migration 執行成功
- **WHEN** 執行 `php artisan migrate`
- **THEN** `diving_offers` 表上存在 `provider_id` 索引
#### Scenario: Provider 課程列表查詢走索引
- **WHEN** 系統執行 `DivingOffer::where('provider_id', $providerId)->get()`
- **THEN** MySQL `EXPLAIN` 顯示使用 `provider_id` 索引,`type``ref`