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

523 lines
14 KiB
Markdown

# 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`](frontend/types/index.ts)
#### 郵件帳號類型
```typescript
// 更新為符合後端 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
}
```
#### 系統權限類型 (新增)
```typescript
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`](frontend/services/email-accounts.ts)
**提供的方法**:
- `list(params?)` - 獲取郵件帳號列表 (支援分頁、篩選、搜尋)
- `get(id)` - 獲取郵件帳號詳情
- `create(data)` - 創建郵件帳號
- `update(id, data)` - 更新郵件帳號
- `updateQuota(id, data)` - 更新郵件配額
- `delete(id)` - 停用郵件帳號 (軟刪除)
- `getByEmployee(employeeId)` - 獲取員工的所有郵件帳號
**查詢參數**:
```typescript
interface EmailAccountListParams {
page?: number
page_size?: number
employee_id?: number
is_active?: boolean
search?: string
}
```
#### 系統權限服務
**檔案**: [`services/permissions.ts`](frontend/services/permissions.ts)
**提供的方法**:
- `list(params?)` - 獲取權限列表 (支援分頁、篩選)
- `get(id)` - 獲取權限詳情
- `create(data)` - 創建權限
- `update(id, data)` - 更新權限
- `delete(id)` - 刪除權限
- `getByEmployee(employeeId)` - 獲取員工的所有系統權限
- `batchCreate(data)` - 批量創建權限
- `getSystems()` - 獲取可授權的系統列表
**查詢參數**:
```typescript
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`](frontend/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`](frontend/.env.local)
```bash
# 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` | 系統列表 |
---
## 📝 使用範例
### 郵件帳號服務使用
```typescript
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
})
```
### 系統權限服務使用
```typescript
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 整合範例
```typescript
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 刷新
---
## 📋 檢查清單
- [x] TypeScript 類型定義更新
- [x] 郵件帳號服務建立
- [x] 系統權限服務建立
- [x] API 客戶端確認
- [x] 環境配置確認
- [x] 與後端 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
**審核者**: (待填寫)