feat(vmis): 租戶自動開通完整流程 + Admin Portal SSO + NC 行事曆訂閱

Backend:
- schedule_tenant: NC 新容器自動 pgsql 安裝 (_nc_db_check 全新容器處理)
- schedule_tenant: NC 初始化加入 Redis + APCu memcache 設定 (修正 OIDC invalid_state)
- schedule_tenant: 新租戶 KC realm 自動設定 accessCodeLifespan=600s (修正 authentication_expired)
- schedule_account: NC Mail 帳號自動設定 (nc_mail_result/nc_mail_done_at)
- schedule_account: NC 台灣國定假日行事曆自動訂閱 (CalDAV MKCALENDAR)
- nextcloud_client: 新增 subscribe_calendar() CalDAV 訂閱方法
- settings: 新增系統設定 API (site_title/version/timezone/SSO/Keycloak)
- models/result: 新增 nc_mail_result, nc_mail_done_at 欄位
- alembic: 遷移 002(system_settings) 003(keycloak_admin) 004(nc_mail_result)

Frontend (Admin Portal):
- 新增完整管理後台 (index/tenants/accounts/servers/schedules/logs/settings/system-status)
- api.js: Keycloak JS Adapter SSO 整合 (PKCE/S256, fallback KC JS 來源, 自動 token 更新)
- index.html: Promise.allSettled 取代 Promise.all,防止單一 API 失敗影響整頁
- 所有頁面加入 try/catch + toast 錯誤處理
- 新增品牌 LOGO 與 favicon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VMIS Developer
2026-03-15 15:31:37 +08:00
parent 42d1420f9c
commit 62baadb06f
53 changed files with 5638 additions and 195 deletions

View File

@@ -0,0 +1,254 @@
# WebMail Keycloak SSO 整合指南
本指南說明如何將 Roundcube WebMail 與 Keycloak SSO 整合。
## 前置條件
已完成:
- ✅ Keycloak Client 已建立
- ✅ Client ID: `webmail`
- ✅ Client Secret: `CDUcB68SZUEZ2zmiQV4cz22czjw6Sn1q`
- ✅ Realm: `vmis-admin`
## 步驟 1: 連接到 WebMail 主機
```bash
ssh 10.1.0.254
```
## 步驟 2: 查看 Roundcube 容器
```bash
docker ps | grep -i roundcube
```
記錄容器名稱 (例如: `roundcube``webmail`)
## 步驟 3: 查看 Roundcube 配置掛載路徑
```bash
docker inspect <容器名稱> | grep -A 5 "Mounts"
```
找出配置檔案的掛載路徑 (通常是 `/var/www/html/config` 或類似路徑)
## 步驟 4: 安裝 OAuth2 插件
### 方法 A: 如果 Roundcube 使用官方 Docker 映像
1. 進入容器:
```bash
docker exec -it <容器名稱> bash
```
2. 使用 Composer 安裝 OAuth2 插件:
```bash
cd /var/www/html
composer require roundcube/oauth2
```
3. 啟用插件 (編輯 config/config.inc.php):
```bash
vi config/config.inc.php
```
找到 `$config['plugins']` 行,加入 `'oauth2'`:
```php
$config['plugins'] = array(
'oauth2',
// ... 其他插件
);
```
### 方法 B: 如果無法使用 Composer
手動下載並安裝插件:
```bash
cd /var/www/html/plugins
git clone https://github.com/roundcube/roundcubemail-oauth2.git oauth2
```
## 步驟 5: 配置 OAuth2 插件
建立或編輯 `config/oauth2.inc.php`:
```bash
vi /var/www/html/config/oauth2.inc.php
```
加入以下內容:
```php
<?php
/**
* Roundcube OAuth2 Configuration
* Keycloak SSO Integration for WebMail
*/
$config['oauth2'] = array(
'provider' => 'generic',
'client_id' => 'webmail',
'client_secret' => 'CDUcB68SZUEZ2zmiQV4cz22czjw6Sn1q',
'auth_uri' => 'https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/auth',
'token_uri' => 'https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/token',
'identity_uri' => 'https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/userinfo',
'redirect_uri' => 'https://webmail.porscheworld.tw/index.php/login/oauth',
'scope' => 'openid email profile',
'login_redirect' => true,
'username_field' => 'email',
'identity_fields' => array(
'email' => 'email',
'name' => 'name',
'username' => 'preferred_username'
),
);
// 自動登入 (可選)
$config['oauth2_auto_login'] = true;
// 允許傳統登入 (開發階段建議保留)
$config['oauth2_allow_traditional_login'] = true;
```
## 步驟 6: 修改 Roundcube 主配置
編輯 `config/config.inc.php`:
```bash
vi /var/www/html/config/config.inc.php
```
確保以下設定:
```php
// 啟用插件
$config['plugins'] = array(
'oauth2',
// ... 其他插件
);
// IMAP 設定 (根據實際郵件伺服器調整)
$config['default_host'] = 'ssl://10.1.0.254';
$config['default_port'] = 993;
// SMTP 設定
$config['smtp_server'] = 'tls://10.1.0.254';
$config['smtp_port'] = 587;
// 使用者名稱網域 (根據 Keycloak 使用者的 email 設定)
$config['username_domain'] = array(
'porscheworld.tw' => 'porscheworld.tw',
'lab.taipei' => 'lab.taipei',
'ease.taipei' => 'ease.taipei',
);
// 自動完成郵件地址
$config['mail_domain'] = 'lab.taipei';
```
## 步驟 7: 設定郵件地址對應規則
Keycloak 使用者的 `email` 屬性應該對應到郵件伺服器的帳號。
### 確保 Keycloak 使用者有正確的 email
1. 登入 Keycloak Admin Console:
```
https://auth.lab.taipei/admin
```
2. 進入 `vmis-admin` Realm
3. 檢查使用者的 Email 欄位,例如:
- Username: `sysadmin`
- Email: `admin@lab.taipei`
這個 Email 必須是郵件伺服器上實際存在的帳號。
## 步驟 8: 重啟 Roundcube 容器
```bash
docker restart <容器名稱>
```
## 步驟 9: 測試 SSO 登入
1. 清除瀏覽器 Cookie
2. 訪問 WebMail:
```
https://webmail.porscheworld.tw
```
3. 應該會自動重導向到 Keycloak 登入頁面:
```
https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/auth?...
```
4. 使用 Keycloak 帳號登入 (例如: `sysadmin`)
5. 登入成功後,應該會重導向回 WebMail 並自動登入郵件帳號
## 故障排除
### 問題 1: 無法重導向到 Keycloak
**檢查:**
- OAuth2 插件是否正確安裝
- `config/oauth2.inc.php` 是否存在且設定正確
- Roundcube 容器日誌: `docker logs <容器名稱>`
### 問題 2: 登入後顯示「Invalid credentials」
**可能原因:**
- Keycloak 使用者的 email 與郵件伺服器帳號不符
- 郵件伺服器密碼與 Keycloak 密碼不同
**解決方案:**
需要實作「密碼同步」或使用「IMAP OAuth2」:
1. **方案 A: 密碼同步** - Keycloak 密碼變更時同步到郵件伺服器
2. **方案 B: IMAP OAuth2** - 郵件伺服器支援 OAuth2 認證 (需要 Docker Mailserver 配置)
### 問題 3: 重導向 URI 不符
**錯誤訊息:**
```
Invalid redirect_uri
```
**解決方案:**
檢查 Keycloak Client 的 Valid Redirect URIs 設定:
```
https://webmail.porscheworld.tw/*
```
## 進階設定
### 整合 HR Portal 多帳號切換
如果要實現「員工可以使用 SSO 登入後,切換不同的郵件帳號」:
1. 安裝 `multi_accounts` 插件
2. 配置 API 端點連接 HR Portal
3. 從 HR Portal 取得員工授權的郵件帳號列表
詳見: [郵件系統設計文件](P:\porscheworld\2.專案設計區\3.MailSystem\郵件系統設計文件.md)
## 相關連結
- Keycloak Admin: https://auth.lab.taipei/admin
- WebMail: https://webmail.porscheworld.tw
- Roundcube OAuth2 Plugin: https://github.com/roundcube/roundcubemail-oauth2
## 完成檢查清單
- [ ] OAuth2 插件已安裝
- [ ] `config/oauth2.inc.php` 設定完成
- [ ] `config/config.inc.php` 啟用插件
- [ ] Keycloak 使用者 email 正確設定
- [ ] Roundcube 容器已重啟
- [ ] SSO 登入測試成功
- [ ] 郵件收發測試成功

View File

@@ -0,0 +1,225 @@
# Virtual MIS - 系統架構設計
**版本**: v1.0
**日期**: 2026-02-27
**狀態**: 規劃中
---
## 系統架構圖
```
┌─────────────────────────────────────────────────────────────┐
│ 客戶企業 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 員工 A │ │ 員工 B │ │ 員工 C │ │ 管理者 │ │
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
└────────┼─────────────┼─────────────┼─────────────┼─────────┘
│ │ │ │
└─────────────┴─────────────┴─────────────┘
┌─────────▼─────────┐
│ Virtual MIS │
│ 統一入口 (SSO) │
└─────────┬─────────┘
┌─────────────────┼─────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ HR │ │ Service │ │ Billing │
│ Portal │ │ Gateway │ │ System │
└────┬────┘ └────┬────┘ └─────────┘
│ │
│ ┌──────────┼──────────┐
│ │ │ │
┌────▼────▼─┐ ┌────▼────┐ ┌──▼──────┐
│ Mail │ │Calendar │ │ Drive │
│ Service │ │Service │ │ Service │
└───────────┘ └─────────┘ └─────────┘
┌────▼────┐
│ Office │
│ Service │
└─────────┘
```
## 核心模組
### 1. 租戶開通系統 (Tenant Onboarding)
**功能**:
- 自動建立租戶資料
- 網域配置與 DNS 設定
- Keycloak Realm 建立
- 服務初始化
- 管理員帳號建立
**API 端點**:
```
POST /api/v1/tenant-onboarding
GET /api/v1/tenant-onboarding/{tenant_id}
PUT /api/v1/tenant-onboarding/{tenant_id}
DELETE /api/v1/tenant-onboarding/{tenant_id}
```
### 2. 服務整合閘道 (Service Integration Gateway)
**功能**:
- Mail System API 整合
- Calendar System API 整合
- Drive System API 整合
- Office System API 整合
- 服務健康檢查
- 統一錯誤處理
**API 端點**:
```
POST /api/v1/services/mail/create-account
POST /api/v1/services/calendar/create-calendar
POST /api/v1/services/drive/create-storage
POST /api/v1/services/office/grant-access
GET /api/v1/services/health
```
### 3. 計費管理系統 (Billing System)
**功能**:
- 訂閱方案管理
- 使用量追蹤
- 帳單生成
- 金流整合 (綠界/藍新)
- 自動續約/停權
**API 端點**:
```
POST /api/v1/billing/subscribe
GET /api/v1/billing/invoices
POST /api/v1/billing/payment
GET /api/v1/billing/usage/{tenant_id}
```
### 4. 管理後台 (Admin Portal)
**功能**:
- 租戶管理
- 服務監控儀表板
- 使用量報表
- 客戶支援工單
- 系統配置
**頁面**:
```
/admin/dashboard - 總覽儀表板
/admin/tenants - 租戶管理
/admin/services - 服務狀態
/admin/billing - 計費管理
/admin/support - 客戶支援
```
### 5. Landing Page (行銷頁面)
**功能**:
- 服務介紹
- 定價方案
- 免費試用申請
- 客戶見證
- 聯絡表單
**頁面**:
```
/ - 首頁
/pricing - 定價方案
/features - 功能介紹
/trial - 免費試用
/contact - 聯絡我們
```
## 資料庫設計
### 核心表
1. **tenants** - 租戶資料 (繼承 HR Portal)
2. **subscriptions** - 訂閱記錄
3. **invoices** - 帳單記錄
4. **service_usage** - 服務使用記錄
5. **support_tickets** - 客戶支援工單
## 技術選型
### 後端
- **語言**: Python 3.11+
- **框架**: FastAPI 0.115+
- **ORM**: SQLAlchemy 2.0+
- **驗證**: Pydantic v2
- **任務隊列**: Celery + Redis
### 前端
- **框架**: Next.js 15 (App Router)
- **UI 庫**: Tailwind CSS + shadcn/ui
- **狀態管理**: React Hooks + Context
- **表單**: React Hook Form + Zod
### 基礎設施
- **容器化**: Docker + Docker Compose
- **反向代理**: Nginx
- **SSL**: Let's Encrypt (Certbot)
- **監控**: Prometheus + Grafana
## 部署架構
```
Internet
┌───────────────┐
│ Nginx Proxy │ (SSL Termination)
└───────┬───────┘
┌───┴───┬───────┬───────┬───────┐
│ │ │ │ │
Admin Landing API HR Services
Portal Page Gateway Portal (Mail/Cal/Drive)
```
## 擴展性設計
### 水平擴展
- API Gateway 支援多實例
- 使用 Redis 作為共享快取
- 資料庫讀寫分離
### 垂直擴展
- 服務模組化設計
- 微服務架構準備
- 容器資源動態調整
## 安全性設計
1. **認證授權**:
- Keycloak SSO
- JWT Token
- RBAC 權限控制
2. **資料安全**:
- 資料加密傳輸 (TLS)
- 敏感資料加密儲存
- 定期備份
3. **網路安全**:
- CORS 配置
- Rate Limiting
- DDoS 防護
## 監控與告警
- **服務健康**: 每分鐘檢查
- **使用量追蹤**: 即時記錄
- **錯誤日誌**: 集中管理
- **性能指標**: CPU/記憶體/網路
---
**下一步**:
1. 詳細 API 規格設計
2. 資料庫 Schema 設計
3. 開發環境建置

View File

@@ -0,0 +1,213 @@
# Virtual MIS - 商業計畫
**版本**: v1.0
**日期**: 2026-02-27
---
## 市場分析
### 目標市場
**主要客群**:
1. 新創公司 (5-20人)
- 需要專業 IT 服務但沒有專職 IT 人員
- 預算有限,希望降低 IT 成本
2. 中小企業 (20-50人)
- 現有 IT 資源不足
- 需要現代化的協作工具
3. 遠距團隊
- 分散式辦公需求
- 需要統一的協作平台
### 市場規模
- 台灣中小企業數量: 約 159 萬家 (2024)
- 潛在客戶 (10-100人企業): 約 50 萬家
- 目標市場滲透率 (3年): 0.1% = 500 家
- 預估年營收 (3年): NT$ 9,000,000
## 競爭分析
### 主要競爭對手
| 服務商 | 優勢 | 劣勢 | 我們的差異化 |
|--------|------|------|-------------|
| Google Workspace | 知名度高、整合度好 | 價格較高、資料在國外 | 本地化、價格優勢 |
| Microsoft 365 | 功能完整、企業級 | 複雜度高、價格高 | 簡單易用、中小企業專注 |
| Zoho Workplace | 價格便宜 | 介面較舊、支援不佳 | 更好的 UX、在地支援 |
### 我們的優勢
1. **價格優勢**: 比 Google/Microsoft 便宜 50%
2. **在地化**: 資料存放台灣、中文支援
3. **整合性**: 一站式解決方案
4. **彈性**: 可客製化配置
5. **支援**: 在地技術支援
## 商業模式
### 訂閱制 SaaS
**定價策略**:
#### 測試期方案 (前 6 個月)
| 方案 | 人數 | 月費 | 年費 (85折) | 包含服務 |
|------|------|------|------------|---------|
| 基礎版 | 10人 | NT$ 500 | NT$ 5,100 | HR + Mail + Drive (5GB/人) |
| 標準版 | 50人 | NT$ 1,500 | NT$ 15,300 | 基礎 + Calendar + Office |
| 企業版 | 100人 | NT$ 3,000 | NT$ 30,600 | 標準 + 優先支援 + 10GB/人 |
#### 正式方案 (6 個月後)
| 方案 | 人數 | 月費 | 年費 (85折) |
|------|------|------|------------|
| 基礎版 | 10人 | NT$ 800 | NT$ 8,160 |
| 標準版 | 50人 | NT$ 2,500 | NT$ 25,500 |
| 企業版 | 100人 | NT$ 5,000 | NT$ 51,000 |
### 額外服務
- **超量使用**: NT$ 50/人/月
- **額外儲存**: NT$ 100/10GB/月
- **客製化開發**: 另報價
- **專業服務**: NT$ 2,000/小時
## 營收預測
### 第一年 (2026)
| 月份 | 付費客戶 | 平均月費 | 月營收 | 累計營收 |
|------|---------|---------|--------|---------|
| M1-2 | 0 | - | NT$ 0 | NT$ 0 |
| M3 | 3 | NT$ 1,000 | NT$ 3,000 | NT$ 3,000 |
| M4 | 5 | NT$ 1,000 | NT$ 5,000 | NT$ 8,000 |
| M5 | 8 | NT$ 1,000 | NT$ 8,000 | NT$ 16,000 |
| M6 | 12 | NT$ 1,200 | NT$ 14,400 | NT$ 30,400 |
| M7 | 15 | NT$ 1,200 | NT$ 18,000 | NT$ 48,400 |
| M8 | 20 | NT$ 1,500 | NT$ 30,000 | NT$ 78,400 |
| M9 | 25 | NT$ 1,500 | NT$ 37,500 | NT$ 115,900 |
| M10 | 30 | NT$ 1,500 | NT$ 45,000 | NT$ 160,900 |
| M11 | 35 | NT$ 1,500 | NT$ 52,500 | NT$ 213,400 |
| M12 | 40 | NT$ 1,500 | NT$ 60,000 | NT$ 273,400 |
**第一年總營收**: NT$ 273,400
### 第二年目標
- 客戶數: 100 家
- 平均月費: NT$ 2,000
- 年營收: NT$ 2,400,000
### 第三年目標
- 客戶數: 200 家
- 平均月費: NT$ 2,500
- 年營收: NT$ 6,000,000
## 成本結構
### 固定成本 (月)
- 伺服器租用: NT$ 10,000
- 網路頻寬: NT$ 5,000
- SSL 憑證: NT$ 1,000
- 備份儲存: NT$ 2,000
- **小計**: NT$ 18,000/月
### 變動成本
- 客服人力: NT$ 50/客戶/月
- 技術支援: NT$ 30/客戶/月
- 行銷推廣: 營收的 20%
### 損益平衡點
固定成本 / (平均月費 - 變動成本) = 18,000 / (1,500 - 80) ≈ **13 個客戶**
## 行銷策略
### 獲客管道
1. **內容行銷** (免費)
- 撰寫技術部落格
- SEO 優化
- 社群媒體經營
2. **口碑推薦** (低成本)
- 推薦獎勵計畫
- 客戶見證影片
- 案例研究分享
3. **付費廣告** (初期投入)
- Google Ads (關鍵字: 虛擬辦公室、企業郵件)
- Facebook Ads (目標: 中小企業主)
- LinkedIn Ads (B2B 客戶)
4. **合作夥伴**
- 會計師事務所
- 企業顧問公司
- 創業加速器
### 客戶獲取成本 (CAC)
- 目標 CAC: NT$ 3,000/客戶
- 客戶終身價值 (LTV): NT$ 36,000 (假設留存 2 年)
- LTV/CAC 比: 12:1 (健康指標)
## 風險評估
### 主要風險
1. **技術風險**
- 服務穩定性問題
- 資料安全疑慮
- **應對**: 完善測試、定期備份、安全稽核
2. **市場風險**
- 客戶獲取困難
- 競爭加劇
- **應對**: 差異化定位、優質服務
3. **財務風險**
- 初期現金流不足
- 收款困難
- **應對**: 預收年費、自動扣款
## 里程碑
### Phase 1: MVP (Week 1-6)
- [ ] 完成系統開發
- [ ] 內部測試
- [ ] 準備行銷素材
### Phase 2: Beta (Week 7-8)
- [ ] 招募 5 家測試客戶
- [ ] 收集回饋改進
- [ ] 優化使用者體驗
### Phase 3: Launch (Week 9-12)
- [ ] 正式對外發布
- [ ] 啟動行銷活動
- [ ] 目標達成 10 個付費客戶
### Phase 4: Growth (Month 4-12)
- [ ] 持續優化產品
- [ ] 擴大行銷投入
- [ ] 目標達成 40 個付費客戶
## 退場策略
1. **被收購**: 目標估值 NT$ 30,000,000 (3 年後)
2. **持續經營**: 建立穩定現金流
3. **技術授權**: 授權給大型企業
---
**下一步行動**:
1. 完成 MVP 開發
2. 準備行銷素材
3. 洽談 Beta 測試客戶

373
docs/開發規範.md Normal file
View File

@@ -0,0 +1,373 @@
# Virtual MIS - 開發規範
**版本**: v1.0
**日期**: 2026-02-27
---
## 專案結構
```
virtual-mis/
├── backend/ # 後端服務
│ ├── app/
│ │ ├── api/ # API 路由
│ │ │ ├── v1/
│ │ │ │ ├── tenant_onboarding.py
│ │ │ │ ├── service_integration.py
│ │ │ │ └── billing.py
│ │ │ └── router.py
│ │ ├── core/ # 核心配置
│ │ │ ├── config.py
│ │ │ ├── security.py
│ │ │ └── dependencies.py
│ │ ├── db/ # 資料庫
│ │ │ ├── base.py
│ │ │ └── session.py
│ │ ├── models/ # ORM 模型
│ │ ├── schemas/ # Pydantic Schema
│ │ ├── services/ # 業務邏輯
│ │ └── utils/ # 工具函數
│ ├── alembic/ # 資料庫遷移
│ ├── tests/ # 測試
│ ├── .env # 環境變數
│ ├── requirements.txt # 依賴套件
│ └── main.py # 應用入口
├── frontend/ # 前端服務
│ ├── admin-portal/ # 管理後台
│ │ ├── app/ # Next.js App Router
│ │ ├── components/ # React 元件
│ │ ├── lib/ # 工具函數
│ │ ├── public/ # 靜態資源
│ │ └── package.json
│ │
│ └── landing-page/ # 行銷頁面
│ ├── app/
│ ├── components/
│ └── package.json
└── docs/ # 文件
├── architecture/ # 架構設計
├── api-specs/ # API 規格
├── business/ # 商業計畫
└── deployment/ # 部署文件
```
## 開發環境
### 後端環境
**Python 版本**: 3.11+
**Port 配置**:
- 開發環境: `10281`
- 測試環境: `10282`
**資料庫**:
- Host: `10.1.0.20`
- Port: `5433`
- Database: `virtual_mis`
- User: `admin`
**啟動方式**:
```bash
cd D:\_Develop\porscheworld_develop\virtual-mis\backend
START_BACKEND.bat
```
### 前端環境
**Node.js 版本**: 20+
**Port 配置**:
- Admin Portal: `10280`
- Landing Page: `10290`
**啟動方式**:
```bash
cd D:\_Develop\porscheworld_develop\virtual-mis\frontend\admin-portal
START_FRONTEND.bat
```
## 編碼規範
### Python (後端)
1. **命名規範**:
- 檔案名稱: `snake_case.py`
- 類別名稱: `PascalCase`
- 函數名稱: `snake_case`
- 常數: `UPPER_CASE`
2. **程式碼風格**:
- 使用 Black 格式化
- 遵循 PEP 8
- 最大行長: 100 字元
3. **型別標註**:
```python
def get_tenant(tenant_id: int) -> Optional[Tenant]:
"""取得租戶資料"""
pass
```
4. **文件字串**:
```python
def create_tenant(data: TenantCreate) -> Tenant:
"""
建立新租戶
Args:
data: 租戶建立資料
Returns:
Tenant: 建立的租戶物件
Raises:
ValueError: 租戶代碼已存在
"""
pass
```
### TypeScript (前端)
1. **命名規範**:
- 檔案名稱: `kebab-case.tsx`
- 元件名稱: `PascalCase`
- 函數名稱: `camelCase`
- 介面: `PascalCase` (前綴 I 可選)
2. **元件結構**:
```typescript
'use client'
import { useState } from 'react'
interface Props {
title: string
onSubmit: (data: FormData) => void
}
export default function MyComponent({ title, onSubmit }: Props) {
const [loading, setLoading] = useState(false)
return (
<div>
<h1>{title}</h1>
</div>
)
}
```
3. **型別定義**:
```typescript
// types/tenant.ts
export interface Tenant {
id: number
code: string
name: string
status: 'trial' | 'active' | 'suspended'
}
```
## API 設計規範
### RESTful 規範
**URL 命名**:
- 使用名詞複數: `/api/v1/tenants`
- 使用 kebab-case: `/api/v1/tenant-onboarding`
- 版本控制: `/api/v1/`, `/api/v2/`
**HTTP 方法**:
- `GET`: 查詢資料
- `POST`: 建立資料
- `PUT`: 完整更新
- `PATCH`: 部分更新
- `DELETE`: 刪除資料
**回應格式**:
```json
{
"success": true,
"data": {
"id": 1,
"name": "測試公司"
},
"message": "操作成功"
}
```
**錯誤回應**:
```json
{
"success": false,
"error": {
"code": "TENANT_NOT_FOUND",
"message": "找不到指定的租戶",
"details": {}
}
}
```
### Schema 設計
**命名規範**:
- Base Schema: `TenantBase`
- Create Schema: `TenantCreate`
- Update Schema: `TenantUpdate`
- Response Schema: `TenantResponse`
**範例**:
```python
from pydantic import BaseModel, Field
class TenantBase(BaseModel):
"""租戶基礎 Schema"""
code: str = Field(..., max_length=50, description="租戶代碼")
name: str = Field(..., max_length=200, description="公司名稱")
class TenantCreate(TenantBase):
"""建立租戶 Schema"""
admin_email: str = Field(..., description="管理員郵箱")
class TenantResponse(TenantBase):
"""租戶回應 Schema"""
id: int
status: str
created_at: datetime
model_config = ConfigDict(from_attributes=True)
```
## 資料庫規範
### Migration 管理
**命名規則**:
```
{timestamp}_{description}.py
例如: 20260227_create_subscriptions_table.py
```
**Migration 內容**:
```python
def upgrade() -> None:
"""升級操作"""
op.create_table(
'subscriptions',
sa.Column('id', sa.Integer(), primary_key=True),
sa.Column('tenant_id', sa.Integer(), nullable=False),
# ...
)
def downgrade() -> None:
"""降級操作"""
op.drop_table('subscriptions')
```
### 表格命名
- 使用複數形式: `subscriptions`, `invoices`
- 使用 snake_case
- 加入前綴表示模組: `billing_invoices`
## Git 工作流程
### 分支策略
- `master`: 生產環境
- `develop`: 開發環境
- `feature/*`: 功能開發
- `hotfix/*`: 緊急修復
### Commit 訊息
**格式**:
```
<類型>: <簡短描述>
<詳細說明>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
```
**類型**:
- `feat`: 新功能
- `fix`: 錯誤修復
- `docs`: 文件更新
- `refactor`: 重構
- `test`: 測試
- `chore`: 雜項
**範例**:
```
feat: 新增租戶開通 API
實作租戶自動開通功能,包含:
- 建立租戶資料
- 配置網域
- 建立 Keycloak Realm
- 初始化服務
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
```
## 測試規範
### 單元測試
**覆蓋率目標**: 80%+
**測試檔案命名**:
```
tests/
├── unit/
│ ├── test_tenant_service.py
│ └── test_billing_service.py
└── integration/
├── test_api_tenants.py
└── test_api_billing.py
```
**測試範例**:
```python
import pytest
from app.services.tenant_service import create_tenant
def test_create_tenant_success():
"""測試建立租戶成功"""
data = TenantCreate(
code="testcompany",
name="測試公司",
admin_email="admin@test.com"
)
tenant = create_tenant(data)
assert tenant.code == "testcompany"
assert tenant.status == "trial"
```
## 安全規範
1. **環境變數**: 所有敏感資訊放在 `.env`
2. **密碼處理**: 使用 bcrypt 雜湊
3. **API 驗證**: 所有 API 需要 JWT token
4. **輸入驗證**: 使用 Pydantic 驗證所有輸入
5. **SQL 注入**: 使用 ORM避免原生 SQL
## 文件規範
1. **README.md**: 每個專案必須包含
2. **API 文件**: 使用 FastAPI 自動生成 (Swagger)
3. **程式碼註解**: 複雜邏輯必須註解
4. **設計文件**: 重要功能需要設計文件
---
**參考資源**:
- [FastAPI 官方文件](https://fastapi.tiangolo.com/)
- [Next.js 官方文件](https://nextjs.org/docs)
- [Python PEP 8](https://peps.python.org/pep-0008/)