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>
523 lines
14 KiB
Markdown
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
|
|
**審核者**: (待填寫)
|