Major Features: - ✅ Multi-tenant architecture (tenant isolation) - ✅ Employee CRUD with lifecycle management (onboarding/offboarding) - ✅ Department tree structure with email domain management - ✅ Company info management (single-record editing) - ✅ System functions CRUD (permission management) - ✅ Email account management (multi-account per employee) - ✅ Keycloak SSO integration (auth.lab.taipei) - ✅ Redis session storage (10.1.0.254:6379) - Solves Cookie 4KB limitation - Cross-system session sharing - Sliding expiration (8 hours) - Automatic token refresh Technical Stack: Backend: - FastAPI + SQLAlchemy - PostgreSQL 16 (10.1.0.20:5433) - Keycloak Admin API integration - Docker Mailserver integration (SSH) - Alembic migrations Frontend: - Next.js 14 (App Router) - NextAuth 4 with Keycloak Provider - Redis session storage (ioredis) - Tailwind CSS Infrastructure: - Redis 7 (10.1.0.254:6379) - Session + Cache - Keycloak 26.1.0 (auth.lab.taipei) - Docker Mailserver (10.1.0.254) Architecture Highlights: - Session管理由 Keycloak + Redis 統一控制 - 支援多系統 (HR/WebMail/Calendar/Drive/Office) 共享 session - Token 自動刷新,異質服務整合 - 未來可無縫遷移到雲端 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
12 KiB
12 KiB
🏢 人資管理系統架構設計
📋 系統概述
HR Portal - 整合 Keycloak SSO 的企業人資管理平台
核心功能
- ✅ SSO 統一登入 - Keycloak OAuth2/OIDC
- 👤 員工基本資料管理
- 📧 電子郵件帳號管理 - Docker Mailserver 整合
- 💾 網路硬碟配額管理 - NAS 整合
- 🔐 系統權限管理
- 📊 個人化儀表板
🏗️ 技術架構
技術堆疊
前端
- 框架: React 18 + TypeScript
- UI 庫: Ant Design / Material-UI
- 狀態管理: React Query + Zustand
- 路由: React Router v6
- HTTP: Axios
- 認證: @react-keycloak/web
後端
- 框架: FastAPI (Python 3.11+)
- 資料庫: PostgreSQL 16
- ORM: SQLAlchemy 2.0
- 認證: python-keycloak
- API 文檔: OpenAPI/Swagger
基礎設施
- 反向代理: Traefik
- SSO: Keycloak
- 郵件: Docker Mailserver
- 儲存: Synology NAS (WebDAV/SMB)
- 容器化: Docker + Docker Compose
🗂️ 資料庫設計
員工資料表 (employees)
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
keycloak_user_id UUID UNIQUE NOT NULL, -- Keycloak User ID
employee_id VARCHAR(20) UNIQUE NOT NULL, -- 員工編號
username VARCHAR(100) UNIQUE NOT NULL, -- 登入帳號
-- 基本資料
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
chinese_name VARCHAR(50),
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(20),
mobile VARCHAR(20),
-- 任職資訊
department VARCHAR(100),
position VARCHAR(100),
job_title VARCHAR(100),
employment_type VARCHAR(20), -- full-time, part-time, contractor
hire_date DATE,
termination_date DATE,
-- 狀態
status VARCHAR(20) DEFAULT 'active', -- active, inactive, suspended
-- 審計欄位
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by VARCHAR(100),
updated_by VARCHAR(100)
);
-- 索引
CREATE INDEX idx_employees_keycloak_id ON employees(keycloak_user_id);
CREATE INDEX idx_employees_status ON employees(status);
CREATE INDEX idx_employees_department ON employees(department);
郵件帳號表 (email_accounts)
CREATE TABLE email_accounts (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
email_address VARCHAR(255) UNIQUE NOT NULL,
mailbox_quota_mb INTEGER DEFAULT 1024, -- 郵箱配額 (MB)
mailbox_used_mb INTEGER DEFAULT 0,
-- 郵件設定
forward_to VARCHAR(255),
auto_reply BOOLEAN DEFAULT FALSE,
auto_reply_message TEXT,
-- 狀態
is_active BOOLEAN DEFAULT TRUE,
-- 審計
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_email_accounts_employee ON email_accounts(employee_id);
網路硬碟表 (network_drives)
CREATE TABLE network_drives (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
-- 硬碟資訊
drive_name VARCHAR(100) NOT NULL, -- 個人硬碟名稱
drive_path VARCHAR(500) NOT NULL, -- NAS 路徑
quota_gb INTEGER DEFAULT 10, -- 配額 (GB)
used_gb DECIMAL(10,2) DEFAULT 0,
-- WebDAV 設定
webdav_url VARCHAR(500),
smb_path VARCHAR(500),
-- 權限
can_share BOOLEAN DEFAULT FALSE,
-- 狀態
is_active BOOLEAN DEFAULT TRUE,
-- 審計
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_network_drives_employee ON network_drives(employee_id);
系統權限表 (system_permissions)
CREATE TABLE system_permissions (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id) ON DELETE CASCADE,
system_name VARCHAR(100) NOT NULL, -- gitea, keycloak, portainer 等
system_url VARCHAR(500),
access_level VARCHAR(50), -- admin, user, readonly
-- 狀態
is_active BOOLEAN DEFAULT TRUE,
granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
granted_by VARCHAR(100),
UNIQUE(employee_id, system_name)
);
CREATE INDEX idx_system_permissions_employee ON system_permissions(employee_id);
審計日誌表 (audit_logs)
CREATE TABLE audit_logs (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id),
action VARCHAR(50) NOT NULL, -- create, update, delete, login
resource_type VARCHAR(50), -- employee, email, drive
resource_id INTEGER,
old_value JSONB,
new_value JSONB,
ip_address VARCHAR(50),
user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_audit_logs_employee ON audit_logs(employee_id);
CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);
🔐 SSO 認證流程
登入流程
用戶訪問 https://hr.porscheworld.tw
↓
檢查是否已登入 (檢查 Token)
↓
未登入 → 重定向到 Keycloak
↓
Keycloak 認證
↓
返回 Authorization Code
↓
後端用 Code 換取 Access Token
↓
驗證 Token + 取得用戶資訊
↓
查詢/創建員工記錄
↓
返回用戶 Session
↓
顯示個人化儀表板
🌐 API 端點設計
認證相關
GET /api/auth/login - 發起 SSO 登入
GET /api/auth/callback - SSO 回調處理
POST /api/auth/logout - 登出
GET /api/auth/me - 取得當前用戶資訊
GET /api/auth/refresh - 刷新 Token
員工管理
GET /api/employees - 列出員工 (支援分頁/搜尋)
GET /api/employees/:id - 取得員工詳情
POST /api/employees - 創建員工
PUT /api/employees/:id - 更新員工
DELETE /api/employees/:id - 刪除員工
GET /api/employees/me - 取得當前登入員工資訊
PUT /api/employees/me - 更新個人資料
郵件帳號管理
GET /api/emails - 列出所有郵件帳號
GET /api/emails/:id - 取得郵件帳號詳情
POST /api/emails - 創建郵件帳號
PUT /api/emails/:id - 更新郵件帳號
DELETE /api/emails/:id - 刪除郵件帳號
POST /api/emails/:id/quota - 調整郵箱配額
GET /api/emails/me - 取得我的郵件帳號
網路硬碟管理
GET /api/drives - 列出所有網路硬碟
GET /api/drives/:id - 取得硬碟詳情
POST /api/drives - 創建硬碟配額
PUT /api/drives/:id - 更新硬碟設定
DELETE /api/drives/:id - 刪除硬碟配額
GET /api/drives/me - 取得我的硬碟資訊
GET /api/drives/me/usage - 取得硬碟使用量
系統權限管理
GET /api/permissions - 列出權限
POST /api/permissions - 授予權限
DELETE /api/permissions/:id - 撤銷權限
GET /api/permissions/me - 取得我的系統權限列表
儀表板
GET /api/dashboard/stats - 取得統計數據
GET /api/dashboard/activity - 取得最近活動
審計日誌
GET /api/audit-logs - 查詢審計日誌
GET /api/audit-logs/me - 查詢我的操作記錄
🎨 前端頁面結構
公開頁面
/login- 登入頁 (重定向到 Keycloak)/callback- SSO 回調頁面
管理端 (需要 admin 權限)
/admin/dashboard- 管理儀表板/admin/employees- 員工列表/admin/employees/new- 新增員工/admin/employees/:id- 員工詳情/編輯/admin/emails- 郵件帳號管理/admin/drives- 硬碟配額管理/admin/permissions- 權限管理/admin/audit-logs- 審計日誌
個人端 (所有登入用戶)
/- 個人儀表板/profile- 個人資料/my-email- 我的郵件設定/my-drive- 我的網路硬碟/my-systems- 我的系統權限
🔗 系統整合
1. Keycloak 整合
# 創建員工時同步到 Keycloak
def create_employee(employee_data):
# 1. 在 Keycloak 創建用戶
kc_user = keycloak_admin.create_user({
'username': employee_data['username'],
'email': employee_data['email'],
'firstName': employee_data['first_name'],
'lastName': employee_data['last_name'],
'enabled': True
})
# 2. 在本地資料庫創建記錄
employee = Employee(
keycloak_user_id=kc_user['id'],
**employee_data
)
db.add(employee)
return employee
2. Docker Mailserver 整合
# 創建郵件帳號
def create_email_account(employee_id, email, password):
# 1. 在 Docker Mailserver 創建帳號
docker_exec(
'mailserver',
f'setup email add {email} {password}'
)
# 2. 記錄到資料庫
email_account = EmailAccount(
employee_id=employee_id,
email_address=email,
mailbox_quota_mb=1024
)
db.add(email_account)
return email_account
3. NAS 儲存整合
# 創建網路硬碟
def create_network_drive(employee):
username = employee.username
# 1. 在 NAS 創建個人資料夾
nas_path = f'/volume1/homes/{username}'
create_nas_folder(nas_path)
# 2. 設定配額
set_nas_quota(username, quota_gb=10)
# 3. 記錄到資料庫
drive = NetworkDrive(
employee_id=employee.id,
drive_name=f'{username}_personal',
drive_path=nas_path,
quota_gb=10,
webdav_url=f'https://nas.porscheworld.tw/webdav/{username}',
smb_path=f'\\\\10.1.0.30\\homes\\{username}'
)
db.add(drive)
return drive
📦 部署架構
Docker Compose 服務
services:
hr-portal-backend:
image: hr-portal-backend:latest
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://...
- KEYCLOAK_URL=https://auth.ease.taipei
- MAILSERVER_HOST=10.1.0.254
- NAS_HOST=10.1.0.30
labels:
- "traefik.enable=true"
- "traefik.http.routers.hr-api.rule=Host(`hr.porscheworld.tw`) && PathPrefix(`/api`)"
hr-portal-frontend:
image: hr-portal-frontend:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.hr-web.rule=Host(`hr.porscheworld.tw`)"
hr-portal-db:
image: postgres:16
volumes:
- hr-db-data:/var/lib/postgresql/data
網域配置
https://hr.porscheworld.tw - 前端應用
https://hr.porscheworld.tw/api - 後端 API
https://auth.ease.taipei - Keycloak SSO (已有)
🚀 開發流程
階段 1: 基礎設施
- ✅ 資料庫 Schema
- ✅ Keycloak Client 設定
- ✅ 後端 API 框架
階段 2: 核心功能
- ✅ SSO 認證整合
- ✅ 員工 CRUD
- ✅ 郵件帳號管理
- ✅ 網路硬碟管理
階段 3: 前端開發
- ✅ 管理端介面
- ✅ 個人端介面
- ✅ 儀表板
階段 4: 整合測試
- ✅ 端到端測試
- ✅ 效能測試
- ✅ 安全測試
階段 5: 部署上線
- ✅ Docker 容器化
- ✅ 生產環境部署
- ✅ 監控告警
📊 使用案例
案例 1: 新員工入職
HR 管理員操作:
1. 登入 HR Portal (SSO)
2. 點擊「新增員工」
3. 填寫基本資料
4. 系統自動:
- 在 Keycloak 創建帳號
- 創建郵件帳號 (user@ease.taipei)
- 配置網路硬碟 (10GB)
- 授予基本系統權限
5. 發送歡迎郵件給新員工
案例 2: 員工自助服務
員工操作:
1. 訪問 https://hr.porscheworld.tw
2. 用 Keycloak 帳號登入
3. 查看個人儀表板:
- 基本資料
- 郵箱使用量: 500MB / 1GB
- 硬碟使用量: 3GB / 10GB
- 可訪問系統列表
4. 更新個人聯絡資訊
5. 設定郵件自動回覆
案例 3: 員工離職
HR 管理員操作:
1. 搜尋員工
2. 點擊「離職處理」
3. 系統自動:
- 停用 Keycloak 帳號
- 停用郵件帳號 (或轉發)
- 備份個人硬碟
- 撤銷所有系統權限
4. 記錄審計日誌
這個架構設計可以作為開發的藍圖,接下來我們可以開始實作! 🚀