docs(openspec): 補齊所有 spec 缺少的 Purpose section #58

Merged
a620906209 merged 2 commits from chore/openspec-spec-purpose-sections into master 2026-08-02 20:14:10 +00:00
42 changed files with 279 additions and 28 deletions
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# account-lockout Specification
## Purpose
定義登入失敗鎖定機制的 Cache Key 命名規範、Fixed Window 計數邏輯、閾值設定與前端錯誤提示,防止暴力破解並避免帳號枚舉攻擊。
## Requirements
### Requirement: Cache Key Namespace
後端 SHALL 使用以下格式的 Cache key`{role}` 僅允許 `member``provider` 兩個值,對應各自的登入端點;`{email}``strtolower(trim($request->email))` 正規化後的結果。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# admin-auth Specification
## Purpose
定義管理員帳號的建立途徑(僅限主機端指令,不提供公開註冊端點)、登入/登出、個人資料查詢與 Bearer Token 有效期規則。
## Requirements
### Requirement: 管理員帳號建立途徑
管理員帳號 SHALL 僅能透過主機端 `php artisan app:create-admin` command 或資料庫 seeder 建立。系統 MUST NOT 提供任何公開的管理員註冊 HTTP 端點(原 `POST /api/admin/register` 已於 2026-06-11 因 P0 安全漏洞移除)。command 建立的密碼門檻為至少 8 碼,高於一般使用者。
@@ -1,4 +1,10 @@
## ADDED Requirements
# admin-offer-management Specification
## Purpose
定義管理員查看全平台課程列表與刪除任意課程的 API 行為,這些操作不受一般教練端點的 provider_id 擁有權限制。
## Requirements
### Requirement: 管理員查看全平台課程列表
後端 SHALL 提供 `GET /api/admin/offers`(需 Bearer tokenrole=admin),回傳所有課程,支援關鍵字搜尋與分頁。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# admin-panel-ui Specification
## Purpose
定義 Admin Panel 前端頁面(登入、儀表板、會員/教練/課程管理)的路由、資料載入與操作行為,以及路由守衛與 Layout 規則。
## Requirements
### Requirement: 管理員登入頁
前端 SHALL 提供 `/admin/login` 頁面,供管理員以 email/password 登入,成功後導向 `/admin/dashboard`
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# admin-stats Specification
## Purpose
定義平台統計數據 API 的行為,回傳總會員數、總教練數、總課程數等核心經營指標,供管理員在 Admin Dashboard 快速掌握平台規模,僅限管理員角色存取。
## Requirements
### Requirement: 平台統計數據 API
後端 SHALL 提供 `GET /api/admin/stats`(需 Bearer tokenrole=admin),回傳平台核心數據。
+8 -2
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# admin-user-management Specification
## Purpose
定義管理員查看與管理會員/教練帳號的 API:列表查詢、詳情查看、啟用停用切換;教練驗證狀態變更改由審核狀態機(見 provider-verification)管理。
## Requirements
### Requirement: 管理員查看會員列表
後端 SHALL 提供 `GET /api/admin/members`(需 Bearer tokenrole=admin),回傳所有 role=member 的用戶,支援關鍵字搜尋與分頁。
@@ -58,7 +64,7 @@
---
### Requirement: 管理員驗證教練(已由審核狀態機取代)
`PUT /api/admin/providers/{id}/toggle-verified` 已於 2026-06-12 移除——單鍵切換允許繞過審核狀態機無原因駁回、未送審直接通過)。教練驗證狀態的變更一律透過 `PUT /api/admin/verifications/{userId}/approve|reject`(見 provider-verification 規格「Admin 審核佇列與裁決」)。
系統 SHALL NOT 提供 `PUT /api/admin/providers/{id}/toggle-verified` 端點(已於 2026-06-12 移除——單鍵切換允許繞過審核狀態機無原因駁回、未送審直接通過)。教練驗證狀態的變更 SHALL 一律透過 `PUT /api/admin/verifications/{userId}/approve|reject`(見 provider-verification 規格「Admin 審核佇列與裁決」)。
#### Scenario: toggle 端點回 404
- **WHEN** 管理員請求 `PUT /api/admin/providers/{id}/toggle-verified`
+8
View File
@@ -1,3 +1,11 @@
# api-cache-layer Specification
## Purpose
定義以 Redis 作為 Laravel 快取驅動,涵蓋 Admin 統計、課程列表、評價分布的快取策略、TTL 與資料異動時的失效時機。
## Requirements
### Requirement: Redis 作為快取驅動
系統 SHALL 使用 Redis 作為 Laravel 快取驅動,取代現有的 `database` driver。
+8
View File
@@ -1,3 +1,11 @@
# booking-chat Specification
## Purpose
定義預約聊天室的完整行為:文字與圖片訊息收發、訊息歷史查詢、狀態轉換時的訊息封存、已讀回執、未讀計數與跨裝置站內/瀏覽器通知。
## Requirements
### Requirement: 發送文字訊息
Member 與 Provider SHALL 能在 `confirmed` 狀態的預約中透過 `POST /api/bookings/{id}/messages` 發送文字訊息。訊息儲存後系統 SHALL 廣播 `MessageSent` event 至 `presence-booking.{id}` 頻道。
+8
View File
@@ -1,3 +1,11 @@
# booking-lifecycle Specification
## Purpose
定義預約系統的七狀態機、建立/確認/拒絕/取消流程、防超賣的名額驗證、Scheduler 自動過期與完成,以及狀態轉換觸發的通知。
## Requirements
### Requirement: Member 送出預約
Member SHALL 能選擇一個開放時段送出預約,系統記錄價格快照。pending 狀態不佔用時段名額。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# coach-offers-api Specification
## Purpose
定義教練課程管理 API 的 provider_id 所有權驗證不變式與 CRUD 端點行為,確保教練只能查看與操作自己的課程。
## Requirements
### Requirement: provider_id 所有權不變式
對單一課程操作端點(show / update / destroy),系統 MUST 依序執行:先以 id 查找課程(不存在回 404),再比對 provider_id(不符回 403)。兩步驟不可合併為單一 WHERE 查詢。`store()` MUST 強制將 `provider_id` 設為 `auth()->id()`,忽略 request body 傳入值。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# coach-portal-ui Specification
## Purpose
定義 Coach Portal 前端頁面(註冊、登入、課程 Dashboard、新增/編輯課程、個人資料)的路由行為與路由守衛規則。
## Requirements
### Requirement: 教練註冊頁
前端 SHALL 提供 `/coach/register` 頁面,供教練填寫帳號資訊與業者資料後申請帳號,成功後導向 `/coach/login`
@@ -1,5 +1,7 @@
# compose-cloud-baseline
## Purpose
Docker Compose 雲端基準設定——healthcheck 使用真實端點、正式 compose 不含開發工具、dev-only 服務透過 override 隔離。
## Requirements
@@ -1,3 +1,11 @@
# course-image-upload Specification
## Purpose
定義課程封面與相簿圖片的伺服器端壓縮規則、上傳/刪除端點行為、課程刪除時的圖片清理,以及圖片透過 host bind mount 持久化的機制。
## Requirements
### Requirement: 伺服器端圖片壓縮
系統 SHALL 在儲存課程封面與相簿圖片前進行壓縮:長邊超過 2048px 時等比縮小至 2048px 內(僅縮不放大),一律轉存 JPEG(quality 85,副檔名 `.jpg`uuid 檔名)。與聊天圖片管線(`scaleDown(2048) + toJpeg(85)`)參數一致,共用 `App\Traits\CompressesImages`
+8
View File
@@ -1,3 +1,11 @@
# course-scheduling Specification
## Purpose
定義教練建立與管理開課時段、取消時段時對既有預約的 cascade 處理、名額自動管理,以及會員查詢可用時段的行為。
## Requirements
### Requirement: Provider 建立開課時段
Provider SHALL 能為自己擁有的 DivingOffer 建立開課時段,指定日期、開始時間、人數上限。
@@ -1,3 +1,11 @@
# db-index-optimization Specification
## Purpose
定義為加速常用查詢(未讀通知計數、教練課程列表)而新增的資料庫複合索引與單欄索引,避免這些高頻查詢隨資料量成長退化為 full table scan。
## Requirements
### Requirement: Notifications 表補複合索引
`notifications` 表 SHALL 新增 `[notifiable_type, notifiable_id, read_at]` 複合索引,以加速 `unreadNotifications()` 查詢。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# diving-offers-api Specification
## Purpose
定義公開的課程列表與課程詳情 API,支援關鍵字搜尋、地區與標籤篩選、分頁,以及前端開發 origin 的 CORS 設定。
## Requirements
### Requirement: 課程列表 API
後端 SHALL 提供公開的 `GET /api/diving-offers` endpoint,回傳分頁的潛水課程列表,支援關鍵字搜尋與篩選,無需認證即可存取。response 中每筆課程包含 `provider_id` 欄位(可為 null)。
+9 -4
View File
@@ -1,9 +1,9 @@
# env-cloud-annotations
`.env.example` 補充雲端部署必要環境變數的說明與正確預設值。
## Purpose
`.env.example` 補充雲端部署必要環境變數的說明與正確預設值,讓開發者與維運人員能一眼看出哪些變數在雲端環境需要調整,避免沿用本機開發預設值導致部署問題。
## Requirements
### Requirement: QUEUE_CONNECTION 預設值為 redis
`.env.example``QUEUE_CONNECTION` SHALL 預設為 `redis`,與 VPS 實際運作設定一致,避免新環境按範本初始化後跑在效能較差的 database queue。
@@ -13,7 +13,7 @@
### Requirement: .env.example 標示雲端必要變數
`.env.example` SHALL 以行內註解或正確預設值標示以下雲端部署時必須明確設定的變數:
- `FILESYSTEM_DISK`雲端應設為 `s3`(預設 `local` 在容器重啟後遺失上傳檔案
- `FILESYSTEM_DISK`**預設維持 `local`**(本機開發環境無 S3 相容服務可用);雲端部署時 SHALL 手動設為 `s3` 並補齊 `AWS_ACCESS_KEY_ID``AWS_SECRET_ACCESS_KEY``AWS_BUCKET``AWS_ENDPOINT``AWS_URL``AWS_USE_PATH_STYLE_ENDPOINT` 等變數,此時上傳行為 SHALL 實際切換至 S3 相容物件儲存(不再只是文件註解提醒,`public` disk 驅動確實依此變數切換
- `LOG_CHANNEL`:預設改為 `stderr`(雲端 logging aggregator 標準;原 `stack/daily` 寫檔案不適合雲端)
- `QUEUE_CONNECTION`:預設為 `redis`
- `SESSION_DRIVER`:預設改為 `redis`(取代 `database`,擴容更高效)
@@ -26,6 +26,11 @@
- **WHEN** 開發者以 `.env.example` 為基礎建立 `.env`,未手動修改 `LOG_CHANNEL`
- **THEN** Laravel 日誌寫入 stderr,可透過 `docker compose logs app` 查看
#### Scenario: 新環境照 .env.example 初始化檔案儲存
- **WHEN** 開發者以 `.env.example` 為基礎建立 `.env`,未手動修改 `FILESYSTEM_DISK`
- **THEN** 上傳檔案寫入本機磁碟,不需任何 S3 credentials 即可完整跑起來
#### Scenario: 操作者閱讀 .env.example 進行雲端部署
- **WHEN** 操作者參照 `.env.example` 設定雲端環境的 `.env`
- **THEN** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值
- **THEN** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值,且 `FILESYSTEM_DISK` 的說明明確指出改為 `s3` 後需一併設定哪些 `AWS_*` 變數
+37
View File
@@ -0,0 +1,37 @@
# file-storage-s3 Specification
## Purpose
使用者上傳檔案(課程圖片、教練證照、聊天室圖片)可透過 `FILESYSTEM_DISK` 環境變數切換存放於本機磁碟或 S3 相容物件儲存,並提供既有檔案的一次性遷移工具。
## Requirements
### Requirement: 上傳檔案可切換為 S3 相容物件儲存
`public` disk SHALL 支援透過 `FILESYSTEM_DISK``AWS_*` 環境變數切換底層儲存為 S3 相容物件儲存(如 Cloudflare R2),且不需修改任何呼叫 `Storage::disk('public')` 的既有程式碼。
#### Scenario: FILESYSTEM_DISK 設為 s3 時上傳走物件儲存
- **WHEN** `.env` 設定 `FILESYSTEM_DISK=s3` 並提供有效的 `AWS_ACCESS_KEY_ID``AWS_SECRET_ACCESS_KEY``AWS_BUCKET``AWS_ENDPOINT`
- **THEN** 課程圖片上傳(`CourseImageController`)、教練證照上傳、預約聊天室圖片上傳皆寫入該 S3 相容物件儲存,本機 `storage/app/public/` 不再新增檔案
#### Scenario: FILESYSTEM_DISK 設為 local 時維持本機磁碟(開發環境預設)
- **WHEN** `.env` 未設定或設定 `FILESYSTEM_DISK=local`
- **THEN** 上傳行為與現行本機磁碟儲存完全一致,不需任何 S3 credentials
#### Scenario: 顯示已上傳檔案的網址依 disk 驅動自動切換
- **WHEN** 呼叫 `CourseImage``ProviderCertification` model 的 `Storage::disk('public')->url(...)`
- **THEN** 回傳的網址依當前 `FILESYSTEM_DISK` 設定,分別指向本機 `/storage/...` 路徑或 S3 相容物件儲存的公開網址
### Requirement: 既有本機檔案可一次性遷移至 S3 相容物件儲存
系統 SHALL 提供一個 artisan command,將 `storage/app/public/` 下既有檔案上傳至當前設定的 S3 相容物件儲存,且不刪除本機原始檔案。
#### Scenario: 執行遷移 command 上傳既有檔案
- **WHEN** 操作者執行 `php artisan storage:migrate-to-s3`
- **THEN** `storage/app/public/` 下所有檔案(不含 `storage:link` 符號連結本身)被上傳至 S3 相容物件儲存的對應相對路徑,且本機檔案保持不變
#### Scenario: 遷移 command 支援 dry-run 預覽
- **WHEN** 操作者執行 `php artisan storage:migrate-to-s3 --dry-run`
- **THEN** command 僅列出將被上傳的檔案清單與總數,不實際執行任何上傳
#### Scenario: 部分檔案上傳失敗不中斷整體流程
- **WHEN** 遷移過程中某個檔案上傳失敗(如網路中斷)
- **THEN** command 記錄該檔案為失敗、繼續處理其餘檔案,結束後印出失敗清單並以非 0 exit code 結束
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# login-rate-limiting Specification
## Purpose
定義登入端點的 IP-based 頻率限制規則:會員/教練每分鐘 10 次、管理員每分鐘 3 次,超過限制回傳 HTTP 429。
## Requirements
### Requirement: 登入頻率限制
後端 SHALL 對所有登入端點套用 IP-based 頻率限制,超過限制時回傳 HTTP 429。Member 與 Provider 每 IP 每分鐘最多 10 次(原定 5 次;帳號鎖定機制上線後已涵蓋暴力破解防護,放寬以容納共享 IP 場景,與實作 `throttle:10,1` 一致);Admin 因影響範圍更廣,限制為每 IP 每分鐘最多 3 次。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# member-portal-ui Specification
## Purpose
定義 Member Portal 前端的完整頁面行為:首頁、課程列表/詳情、登入註冊(含 Google OAuth)、個人資料,以及以 sessionStorage 管理的認證狀態。
## Requirements
### Requirement: 專案基礎建設
前端 SHALL 建立於本 repo 的 `frontend/` 目錄(原規劃獨立 repo,後併入主 repo 以簡化版控與部署),使用 Vue 3 + Vite + Tailwind CSS + Vue Router 4 + Pinia + Axios,並設定 `.env` 指定後端 API base URL。
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# notification-core Specification
## Purpose
定義站內通知的資料模型、通知列表與未讀數 API、標記已讀/刪除操作,以及前端 Bell Icon 與通知中心的顯示與動態輪詢機制。
## Requirements
### Requirement: 通知資料模型
+8 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# notification-email Specification
## Purpose
定義透過 SMTP 非同步寄送 Email 通知的機制,涵蓋本地 Mailpit 攔截、Queue Worker 投遞、Markdown 模板與各事件的觸發條件。
## Requirements
### Requirement: Laravel Mail 設定
@@ -56,6 +62,7 @@ Email 通知 SHALL 透過 Laravel Queue`QUEUE_CONNECTION=database`)非同
---
### Requirement: Email 通知觸發條件與收件人
系統 SHALL 依下表定義的事件、收件人與主旨寄送 Email 通知:
| 事件 | 收件人 | 主旨 |
|------|--------|------|
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# notification-triggers Specification
## Purpose
定義預約與評價各事件(建立、確認、拒絕、取消、完成、收到評價)觸發站內與 Email 通知的時機與收件人,且通知失敗不影響主業務。
## Requirements
### Requirement: 預約建立觸發通知
@@ -1,4 +1,10 @@
## ADDED Requirements
# oauth-state-validation Specification
## Purpose
定義 Google OAuth 登入流程以 state 參數防範 CSRF 攻擊的產生、比對、單次使用與失敗導向規則。
## Requirements
### Requirement: OAuth 授權流程帶 state 參數防 CSRF
後端 SHALL 在 OAuth redirect 前產生隨機 state 字串(至少 32 bytes hex),存入 `session('oauth_state')`,並附加於 OAuth redirect URL 的 `state` query parameter。`stateless()` 呼叫 SHALL 被移除;Socialite 在 `redirect()` 內會自行寫入 `session('state')`,實作 SHALL 在 `redirect()` 呼叫後以同一 state 值覆蓋 `session('state')`,確保 Socialite 內建驗證與手動驗證使用相同值。callback 時 SHALL 從 request 讀取 `state` 並與 `session('oauth_state')` 比對(`hash_equals`);比對成功後 SHALL 立即清除;不符或缺少時 SHALL redirect 至 `/login?error=oauth_failed`
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# provider-auth Specification
## Purpose
定義教練帳號的登入、註冊、登出、個人資料讀取與更新 API,以及 Bearer Token 的 7 天有效期與 refresh 機制。
## Requirements
### Requirement: 教練帳號登入
後端 SHALL 提供 `POST /api/provider/login`,驗證 email/password 並回傳 Sanctum Bearer token,僅限 role=provider 帳號。回傳的 token 有效期為 7 天。
+1 -1
View File
@@ -4,7 +4,7 @@
定義教練資質審核的完整生命週期:教練上傳證照送審、Admin 審核裁決(通過/駁回含原因)、結果通知,以及「未通過審核(approved)之教練的課程不對公開端點曝光、不可接受新預約」的可見性約束。
## ADDED Requirements
## Requirements
### Requirement: 教練驗證狀態機
`provider_profiles.verification_status` SHALL 為四狀態字串:`unsubmitted`(註冊預設)、`pending``approved``rejected`。合法轉移僅限:`unsubmitted→pending`(送審)、`pending→approved`(通過)、`pending→rejected`(駁回)、`rejected→pending`(重新送審,同時清空 `rejection_reason`)、`approved→rejected`(撤銷,原因必填)。原 boolean `is_verified` 欄位移除,API 輸出以 accessor 保留 `is_verified`= status 為 approved)。
+8
View File
@@ -1,3 +1,11 @@
# review-lifecycle Specification
## Purpose
定義評價系統的完整生命週期:新增/修改/刪除評價的資格驗證、課程統計即時重算、匿名公開顯示與分頁排序,以及評價觸發的 Provider 通知。
## Requirements
### Requirement: Member 新增評價
已完成特定課程的 Member SHALL 能對該課程留下一次評價(星等 + 文字)。
+8
View File
@@ -1,3 +1,11 @@
# review-voting Specification
## Purpose
定義會員對評價投「有幫助」票的規則:可取消不可重複、角色限制、不可對自己的評價投票,以及投票狀態隨評價列表回傳。
## Requirements
### Requirement: Member 對評價投「有幫助」票
已登入 **Member**role = memberSHALL 能對評價投「有幫助」票,可取消,不可重複投票。Provider 與 Admin 不可投票。
@@ -1,5 +1,7 @@
# scheduler-container
## Purpose
Laravel Scheduler 以獨立 container 執行,取代 app container 內的 cron daemon。
## Requirements
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# swagger-admin-api Specification
## Purpose
定義 Admin 端點的 Swagger API 文件涵蓋範圍,包含統計、會員/教練/課程/預約/評價管理端點。
## Requirements
### Requirement: Admin 端點 Swagger 文件
@@ -1,4 +1,10 @@
## ADDED Requirements
# swagger-auth-supplement Specification
## Purpose
定義補全 Auth 相關端點的 Swagger 文件涵蓋範圍,包含 Google OAuth 與三角色的密碼變更端點。
## Requirements
### Requirement: Auth 補全端點 Swagger 文件
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# swagger-member-api Specification
## Purpose
定義 Member 端點的 Swagger API 文件涵蓋範圍,包含預約、評價、投票與站內通知端點。
## Requirements
### Requirement: Member 端點 Swagger 文件
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# swagger-provider-api Specification
## Purpose
定義 Provider 端點的 Swagger API 文件涵蓋範圍,包含課程 CRUD、圖片管理、開課時段與預約處理端點。
## Requirements
### Requirement: Provider 端點 Swagger 文件
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# swagger-public-api Specification
## Purpose
定義無需認證的公開端點與全域共用 Schema 的 Swagger API 文件涵蓋範圍,包含課程列表、詳情、評價與時段查詢。
## Requirements
### Requirement: 公開端點 Swagger 文件及共用 Schema
+7 -1
View File
@@ -1,4 +1,10 @@
## ADDED Requirements
# token-refresh Specification
## Purpose
定義三角色(會員/教練/管理員)的 Bearer Token Refresh 端點,以及前端 axios interceptor 的 401 自動 refresh-then-retry 攔截機制。
## Requirements
### Requirement: Member Token Refresh
後端 SHALL 提供 `POST /api/member/refresh`(需有效 Bearer token),revoke 現有 token 並發行新的 7 天 token。
+8
View File
@@ -1,3 +1,11 @@
# user-presence Specification
## Purpose
定義預約聊天室的 Presence Channel 訂閱驗證、在線狀態感知、已讀回執顯示與未讀訊息計數的即時行為。
## Requirements
### Requirement: 訂閱預約 Presence Channel
已認證的 Member 與 Provider SHALL 能透過 Laravel Echo 訂閱 `presence-booking.{booking_id}` 頻道,訂閱時系統 SHALL 驗證使用者確為該預約的參與方。