Files
hr-portal/ARCHITECTURE.md
Porsche Chen 360533393f feat: HR Portal - Complete Multi-Tenant System with Redis Session Storage
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>
2026-02-23 20:12:43 +08:00

12 KiB

🏢 人資管理系統架構設計

📋 系統概述

HR Portal - 整合 Keycloak SSO 的企業人資管理平台

核心功能

  1. SSO 統一登入 - Keycloak OAuth2/OIDC
  2. 👤 員工基本資料管理
  3. 📧 電子郵件帳號管理 - Docker Mailserver 整合
  4. 💾 網路硬碟配額管理 - NAS 整合
  5. 🔐 系統權限管理
  6. 📊 個人化儀表板

🏗️ 技術架構

技術堆疊

前端

  • 框架: 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. 記錄審計日誌

這個架構設計可以作為開發的藍圖,接下來我們可以開始實作! 🚀