Files
CFDivePlatform/openspec/changes/archive/2026-05-10-admin-panel/design.md
T
a620906209 ad2c05779d feat:實作 Admin Panel — 平台管理後台
後端:
- AdminStatsController:總會員/教練/課程數統計 API
- AdminUserController:會員與教練列表、詳情、啟用/停用、教練驗證(toggle 反轉語意)
- AdminOfferController:全平台課程列表與刪除
- routes/api.php:新增 /api/admin/stats、members、providers、offers 等路由

前端(frontend/):
- adminAuth store、adminAxios(第三套獨立認證)
- /admin/* 路由群組 + requiresAdmin guard
- AdminNavBar、AdminLayout
- App.vue:isCoachPage → isBackofficePage(/coach/* 和 /admin/* 皆隱藏會員 NavBar)
- LoginView、DashboardView(統計卡片)
- MembersView、ProvidersView(含驗證操作)、OffersView(含刪除確認)

OpenSpec:
- 新增 specs:admin-auth / admin-user-management / admin-offer-management / admin-stats / admin-panel-ui
- 歸檔:openspec/changes/archive/2026-05-10-admin-panel

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-10 04:07:13 +08:00

126 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
後端 Admin Auth 方法(loginAdmin / logoutAdmin / adminProfile / updateAdminProfile)已存在於 AuthController,路由也已佔位。User model 有 `is_active` 欄位;ProviderProfile 有 `is_verified` 欄位,目前無 API 可修改。前端已有 memberAuth / coachAuth 兩套認證模式,Admin 循同一模式新增第三套。
## Goals / Non-Goals
**Goals:**
- 管理員可登入後台、查看平台數據
- 管理員可列表/搜尋會員與教練,並啟用/停用帳號
- 管理員可驗證教練(設定 ProviderProfile.is_verified
- 管理員可查看全平台課程並刪除違規內容
- 前端 `/admin/*` 有獨立 Layout,不顯示會員 NavBar
**Non-Goals:**
- Admin 帳號自助註冊(透過後端 seeder 或直接 DB 建立)
- 細粒度角色權限(RBAC
- 操作日誌(Audit Log
- 批次操作(批量停用)
## Decisions
### D1Admin Auth 沿用現有 AuthController,不新建 Controller
**決定**`loginAdmin``logoutAdmin``adminProfile``updateAdminProfile` 直接沿用,不修改。
**理由**:方法已存在且邏輯完整,路由也已佔位。
---
### D2:業務邏輯拆到獨立 Controller
**決定**:新增 `AdminUserController`(用戶管理)、`AdminOfferController`(課程管理)、`AdminStatsController`(統計)。
**理由**:與 AuthController 職責分開,避免繼續膨脹。所有方法在開頭驗證 `auth()->user()->role === 'admin'`,非管理員回傳 403。
---
### D3Toggle 語意(啟用/停用、驗證/取消驗證)
**決定**`toggle-active``toggle-verified` 為 PUT 端點,後端直接反轉當前值(`is_active = !is_active`),不接受 body 傳入布林值。
**理由**:UI 是單一按鈕切換狀態,反轉語意最直覺,避免前端傳錯值。
---
### D4adminAuth 獨立 StorelocalStorage key `admin_token` / `admin_user`
**決定**:循 coachAuth 模式,新增第三套獨立 store。
**理由**:三種角色可能在不同 tab 同時使用,共用 store 會互相污染。
---
## Contracts
### API Schema
#### `POST /api/admin/login`(現有)
```
Body: { "email": "...", "password": "..." }
Response 200: { "status": true, "data": { "user": {...}, "token": "...", "token_type": "Bearer" } }
```
#### `GET /api/admin/stats`(需 Bearer tokenrole=admin
```
Response 200:
{
"status": true,
"data": {
"total_members": 120,
"total_providers": 18,
"total_offers": 64
}
}
```
#### `GET /api/admin/members`(需 Bearer tokenrole=admin
```
Query: q(搜尋 name / email, page, per_pagedefault 15
Response 200: { "status": true, "data": [...users with memberProfile], "meta": {...} }
```
#### `GET /api/admin/members/{id}`
```
Response 200: { "status": true, "data": { ...user, profile: {...} } }
Response 404: { "status": false, "message": "用戶不存在" }
```
#### `PUT /api/admin/members/{id}/toggle-active`
```
Response 200: { "status": true, "message": "帳號已停用" | "帳號已啟用", "data": { "is_active": false | true } }
Response 404: { "status": false, "message": "用戶不存在" }
```
#### `GET /api/admin/providers`(同 members 結構,含 providerProfile
#### `GET /api/admin/providers/{id}`
#### `PUT /api/admin/providers/{id}/toggle-active`
#### `PUT /api/admin/providers/{id}/toggle-verified`
```
Response 200: { "status": true, "message": "教練已驗證" | "已取消驗證", "data": { "is_verified": true | false } }
```
#### `GET /api/admin/offers`
```
Query: q(搜尋 title / location, page, per_pagedefault 15
Response 200: { "status": true, "data": [...offers with provider_id], "meta": {...} }
```
#### `DELETE /api/admin/offers/{id}`
```
Response 200: { "status": true, "message": "課程已刪除" }
Response 404: { "status": false, "message": "課程不存在" }
```
---
## Risks / Trade-offs
| 風險 | 緩解策略 |
|------|----------|
| toggle 反轉語意若網路重試,可能連按兩次回到原狀態 | MVP 接受,未來可改為明確 `{ is_active: true/false }` body |
| Admin 帳號只能透過 DB 或 seeder 建立,無自助註冊 | 開發期間用 tinker 建立,正式環境透過 seeder |
| `AdminUserController` 對 member / provider 各需重複驗證邏輯 | 用 private helper method 共用,避免複製貼上 |
| `/admin/*` 頁面無額外安全層(任何人知道路徑都可訪問登入頁) | MVP 接受,route guard 在 frontend 層足夠 |