Files
vmis/docs/開發規範.md
VMIS Developer 62baadb06f 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>
2026-03-15 15:31:37 +08:00

374 lines
8.0 KiB
Markdown
Raw Permalink 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.
# 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/)