Files
hr-portal/Phase_1.3_完成報告.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

14 KiB

HR Portal Phase 1.3 完成報告

階段: Phase 1.3 - 前端專案架構 (React + TypeScript + Tailwind) 完成日期: 2026-02-15 狀態: 完成


📋 執行摘要

完成 HR Portal 前端架構的擴展,為 Phase 1.2 新增的郵件帳號管理和系統權限管理功能建立完整的前端支援。所有類型定義和服務層都已更新,確保與後端 API 完全對接。


完成項目

1. 類型定義更新 (TypeScript Types)

檔案: types/index.ts

郵件帳號類型

// 更新為符合後端 API 結構
export interface EmailAccount extends BaseEntity {
  tenant_id: number
  employee_id: number
  email_address: string
  quota_mb: number
  forward_to?: string | null
  auto_reply?: string | null
  is_active: boolean
  employee_name?: string
  employee_number?: string
}

export interface CreateEmailAccountInput {
  employee_id: number
  email_address: string
  quota_mb?: number
  forward_to?: string
  auto_reply?: string
  is_active?: boolean
}

export interface UpdateEmailAccountInput {
  quota_mb?: number
  forward_to?: string | null
  auto_reply?: string | null
  is_active?: boolean
}

export interface EmailAccountQuotaUpdate {
  quota_mb: number
}

系統權限類型 (新增)

export type SystemName = 'gitea' | 'portainer' | 'traefik' | 'keycloak'
export type AccessLevel = 'admin' | 'user' | 'readonly'

export interface Permission extends BaseEntity {
  tenant_id: number
  employee_id: number
  system_name: SystemName
  access_level: AccessLevel
  granted_at: string
  granted_by?: number | null
  employee_name?: string
  employee_number?: string
  granted_by_name?: string
}

export interface CreatePermissionInput {
  employee_id: number
  system_name: SystemName
  access_level: AccessLevel
  granted_by?: number
}

export interface UpdatePermissionInput {
  access_level: AccessLevel
  granted_by?: number
}

export interface PermissionBatchCreateInput {
  employee_id: number
  permissions: Array<{
    system_name: SystemName
    access_level: AccessLevel
  }>
  granted_by?: number
}

export interface SystemInfo {
  systems: SystemName[]
  access_levels: AccessLevel[]
  system_descriptions: Record<SystemName, string>
  access_level_descriptions: Record<AccessLevel, string>
}

2. 服務層建立 (Service Layer)

郵件帳號服務

檔案: services/email-accounts.ts

提供的方法:

  • list(params?) - 獲取郵件帳號列表 (支援分頁、篩選、搜尋)
  • get(id) - 獲取郵件帳號詳情
  • create(data) - 創建郵件帳號
  • update(id, data) - 更新郵件帳號
  • updateQuota(id, data) - 更新郵件配額
  • delete(id) - 停用郵件帳號 (軟刪除)
  • getByEmployee(employeeId) - 獲取員工的所有郵件帳號

查詢參數:

interface EmailAccountListParams {
  page?: number
  page_size?: number
  employee_id?: number
  is_active?: boolean
  search?: string
}

系統權限服務

檔案: services/permissions.ts

提供的方法:

  • list(params?) - 獲取權限列表 (支援分頁、篩選)
  • get(id) - 獲取權限詳情
  • create(data) - 創建權限
  • update(id, data) - 更新權限
  • delete(id) - 刪除權限
  • getByEmployee(employeeId) - 獲取員工的所有系統權限
  • batchCreate(data) - 批量創建權限
  • getSystems() - 獲取可授權的系統列表

查詢參數:

interface PermissionListParams {
  page?: number
  page_size?: number
  employee_id?: number
  system_name?: SystemName
  access_level?: AccessLevel
}

3. 現有架構確認

技術棧

  • Next.js 15.5.12 - React 框架
  • React 19.2.3 - UI 函式庫
  • TypeScript 5.x - 型別系統
  • Tailwind CSS 4.x - CSS 框架
  • React Query 5.90.21 - 資料獲取與快取
  • Axios 1.13.5 - HTTP 客戶端
  • Keycloak JS 26.2.3 - SSO 認證
  • NextAuth 4.24.11 - 認證框架

目錄結構

frontend/
├── app/                    # Next.js App Router
│   ├── api/               # API 路由
│   ├── auth/              # 認證頁面
│   ├── dashboard/         # 儀表板
│   ├── employees/         # 員工管理
│   ├── departments/       # 部門管理
│   └── business-units/    # 事業部管理
├── components/            # React 元件
│   ├── auth/             # 認證元件
│   ├── employees/        # 員工元件
│   ├── layout/           # 佈局元件
│   └── ui/               # UI 元件
├── hooks/                # React Hooks
├── lib/                  # 工具函式庫
│   ├── api-client.ts    # API 客戶端
│   └── auth.ts          # 認證工具
├── services/             # API 服務層
│   ├── employees.ts     # 員工服務
│   ├── departments.ts   # 部門服務
│   ├── business-units.ts # 事業部服務
│   ├── email-accounts.ts # 郵件帳號服務 ✨ 新增
│   └── permissions.ts   # 系統權限服務 ✨ 新增
├── types/                # TypeScript 類型定義
│   └── index.ts         # 主要類型定義 (已更新)
├── .env.local           # 環境變數配置
├── package.json         # 專案配置
└── tsconfig.json        # TypeScript 配置

API 客戶端

檔案: lib/api-client.ts

功能:

  • 統一的 API 請求管理
  • 自動 Token 注入 (Bearer)
  • 請求/回應攔截器
  • 401 自動導向登入
  • 支援所有 HTTP 方法 (GET, POST, PUT, PATCH, DELETE)

配置:

  • Base URL: http://localhost:10181/api/v1 (開發環境)
  • Timeout: 30 秒
  • Content-Type: application/json

環境配置

檔案: .env.local

# API 配置
NEXT_PUBLIC_API_URL=http://localhost:10181
NEXT_PUBLIC_API_BASE_URL=http://localhost:10181/api/v1

# Keycloak 配置 (開發環境)
NEXT_PUBLIC_KEYCLOAK_URL=https://auth.lab.taipei
NEXT_PUBLIC_KEYCLOAK_REALM=porscheworld
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=hr-portal-web
KEYCLOAK_CLIENT_SECRET=HdQMzecymLixWDJ1dgdH0Ql5rEVU1S5S

# NextAuth 配置
NEXTAUTH_URL=http://localhost:10180
NEXTAUTH_SECRET=ddyW9zuy7sHDMF8HRh60gEoiGBh698Ew6XHKenwp2c0=

# 環境
NEXT_PUBLIC_ENVIRONMENT=development

🏗️ 架構特色

型別安全

  • 完整的 TypeScript 型別定義
  • 與後端 API 完全對應
  • 編譯時型別檢查

服務層分離

  • 清晰的關注點分離
  • 可重用的 API 呼叫
  • 易於測試和維護

React Query 整合 (現有)

  • 自動快取管理
  • 背景資料更新
  • 樂觀更新支援

錯誤處理

  • API 攔截器統一處理
  • 401 自動導向登入
  • 詳細的錯誤訊息

📊 統計數據

新增程式碼

  • 新增檔案: 2 個
    • services/email-accounts.ts (~75 行)
    • services/permissions.ts (~90 行)
  • 更新檔案: 1 個
    • types/index.ts (+85 行)

類型定義

  • EmailAccount 類型: 4 個介面
  • Permission 類型: 6 個介面 + 2 個類型別名
  • 總計新增: 10 個類型定義

服務方法

  • 郵件帳號服務: 7 個方法
  • 系統權限服務: 8 個方法
  • 總計: 15 個服務方法

🔧 技術棧總覽

核心框架

  • Next.js 15.5.12 - React 全端框架
  • React 19.2.3 - UI 函式庫
  • TypeScript 5.x - 靜態型別系統

樣式

  • Tailwind CSS 4.x - Utility-first CSS 框架
  • PostCSS - CSS 處理工具

資料管理

  • React Query (@tanstack/react-query) 5.90.21 - 伺服器狀態管理
  • Axios 1.13.5 - HTTP 客戶端

認證

  • Keycloak JS 26.2.3 - SSO 客戶端
  • NextAuth.js 4.24.11 - 認證框架

🎯 與後端 API 對接

端點對應

郵件帳號管理

前端方法 HTTP 後端端點 說明
list() GET /api/v1/email-accounts 列表查詢
get(id) GET /api/v1/email-accounts/{id} 詳情查詢
create() POST /api/v1/email-accounts 創建帳號
update() PUT /api/v1/email-accounts/{id} 更新帳號
updateQuota() PATCH /api/v1/email-accounts/{id}/quota 更新配額
delete() DELETE /api/v1/email-accounts/{id} 停用帳號
getByEmployee() GET /api/v1/email-accounts/employees/{id}/email-accounts 員工帳號

系統權限管理

前端方法 HTTP 後端端點 說明
list() GET /api/v1/permissions 列表查詢
get(id) GET /api/v1/permissions/{id} 詳情查詢
create() POST /api/v1/permissions 創建權限
update() PUT /api/v1/permissions/{id} 更新權限
delete() DELETE /api/v1/permissions/{id} 刪除權限
getByEmployee() GET /api/v1/permissions/employees/{id}/permissions 員工權限
batchCreate() POST /api/v1/permissions/batch 批量創建
getSystems() GET /api/v1/permissions/systems 系統列表

📝 使用範例

郵件帳號服務使用

import { emailAccountsService } from '@/services/email-accounts'

// 獲取郵件帳號列表
const accounts = await emailAccountsService.list({
  page: 1,
  page_size: 20,
  employee_id: 1,
  is_active: true,
})

// 創建郵件帳號
const newAccount = await emailAccountsService.create({
  employee_id: 1,
  email_address: 'porsche.chen@porscheworld.tw',
  quota_mb: 5120, // 5GB
})

// 更新配額
const updated = await emailAccountsService.updateQuota(1, {
  quota_mb: 10240, // 10GB
})

系統權限服務使用

import { permissionsService } from '@/services/permissions'

// 獲取員工的所有權限
const permissions = await permissionsService.getByEmployee(1)

// 批量授予權限
const newPermissions = await permissionsService.batchCreate({
  employee_id: 1,
  permissions: [
    { system_name: 'gitea', access_level: 'user' },
    { system_name: 'portainer', access_level: 'readonly' },
  ],
  granted_by: 2,
})

// 獲取系統列表
const systemInfo = await permissionsService.getSystems()
console.log(systemInfo.systems) // ['gitea', 'portainer', 'traefik', 'keycloak']

React Query 整合範例

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { emailAccountsService } from '@/services/email-accounts'

// 查詢鉤子
function useEmailAccounts(params) {
  return useQuery({
    queryKey: ['email-accounts', params],
    queryFn: () => emailAccountsService.list(params),
  })
}

// 變更鉤子
function useCreateEmailAccount() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: emailAccountsService.create,
    onSuccess: () => {
      // 重新獲取列表
      queryClient.invalidateQueries({ queryKey: ['email-accounts'] })
    },
  })
}

// 元件中使用
function EmailAccountList() {
  const { data, isLoading, error } = useEmailAccounts({ page: 1 })
  const createMutation = useCreateEmailAccount()

  if (isLoading) return <div>載入中...</div>
  if (error) return <div>錯誤: {error.message}</div>

  return (
    <div>
      {data?.items.map(account => (
        <div key={account.id}>{account.email_address}</div>
      ))}
    </div>
  )
}

🧪 待開發項目 (Phase 2)

UI 元件開發

  • 郵件帳號列表元件
  • 郵件帳號表單元件 (創建/編輯)
  • 配額更新對話框
  • 系統權限列表元件
  • 權限授予表單元件
  • 批量權限授予元件

頁面開發

  • /email-accounts - 郵件帳號管理頁面
  • /permissions - 系統權限管理頁面
  • /employees/[id]/email-accounts - 員工郵件帳號頁面
  • /employees/[id]/permissions - 員工權限頁面

React Hooks

  • useEmailAccounts - 郵件帳號查詢
  • useCreateEmailAccount - 創建郵件帳號
  • useUpdateEmailAccount - 更新郵件帳號
  • usePermissions - 權限查詢
  • useCreatePermission - 創建權限
  • useBatchCreatePermissions - 批量創建權限

表單驗證

  • Email 格式驗證
  • 配額範圍驗證 (1GB-100GB)
  • 系統名稱驗證
  • 權限層級驗證

💡 開發建議

命名規範

  • 元件名稱: PascalCase (如 EmailAccountList)
  • 檔案名稱: kebab-case (如 email-account-list.tsx)
  • 服務方法: camelCase (如 getByEmployee)

程式碼組織

  • 將相關元件放在同一目錄
  • 使用 barrel exports (index.ts)
  • 保持元件單一職責

效能優化

  • 使用 React.memo 避免不必要的重渲染
  • 使用 useCallback/useMemo 快取函式和值
  • 實作虛擬滾動處理大列表

錯誤處理

  • 使用 Error Boundary 捕捉元件錯誤
  • 顯示友善的錯誤訊息
  • 提供重試機制

📚 下一步 (Phase 1.4)

Keycloak SSO 深度整合

  • 完善 Keycloak 認證流程
  • 實作角色與權限檢查
  • 整合 Keycloak 使用者資訊
  • 設定自動 Token 刷新

📋 檢查清單

  • TypeScript 類型定義更新
  • 郵件帳號服務建立
  • 系統權限服務建立
  • API 客戶端確認
  • 環境配置確認
  • 與後端 API 對接規劃
  • UI 元件開發 (Phase 2)
  • 頁面路由建立 (Phase 2)
  • React Hooks 建立 (Phase 2)
  • 單元測試 (Phase 2)
  • E2E 測試 (Phase 2)

🎯 結論

Phase 1.3 成功完成了前端專案架構的擴展,為 Phase 1.2 新增的後端功能建立了完整的前端支援。所有的類型定義和服務層都已就緒,確保前後端完全對接。

現有的技術棧(Next.js 15 + React 19 + TypeScript + Tailwind CSS)提供了強大的開發基礎,配合 React Query 和 Axios,可以高效地開發出現代化的單頁應用。

準備進入 Phase 1.4: Keycloak SSO 整合


報告產出日期: 2026-02-15 撰寫者: Claude AI 審核者: (待填寫)