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:
373
docs/開發規範.md
Normal file
373
docs/開發規範.md
Normal 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/)
|
||||
Reference in New Issue
Block a user