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>
This commit is contained in:
390
test_onboarding.md
Normal file
390
test_onboarding.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# 員工到職流程測試指南 (v3.1)
|
||||
|
||||
## 測試環境
|
||||
|
||||
- **Migration 版本**: 0012 (已執行)
|
||||
- **資料庫**: hr_portal @ 10.1.0.20:5433
|
||||
- **後端 API**: http://localhost:10181/api/v1
|
||||
- **前端**: http://localhost:10180
|
||||
|
||||
---
|
||||
|
||||
## API 端點清單
|
||||
|
||||
### 1. 個人化服務管理
|
||||
|
||||
```bash
|
||||
# 取得所有可用服務
|
||||
GET /api/v1/personal-services/services
|
||||
|
||||
# 查詢使用者已啟用服務
|
||||
GET /api/v1/personal-services/users/{keycloak_user_id}/services
|
||||
|
||||
# 啟用單一服務
|
||||
POST /api/v1/personal-services/users/{keycloak_user_id}/services
|
||||
{
|
||||
"service_id": 1,
|
||||
"quota_gb": 20,
|
||||
"quota_mb": 5120
|
||||
}
|
||||
|
||||
# 批次啟用所有服務
|
||||
POST /api/v1/personal-services/users/{keycloak_user_id}/services/batch-enable
|
||||
{
|
||||
"storage_quota_gb": 20,
|
||||
"email_quota_mb": 5120
|
||||
}
|
||||
|
||||
# 停用服務
|
||||
DELETE /api/v1/personal-services/users/{keycloak_user_id}/services/{service_id}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 員工到職流程 (完整)
|
||||
|
||||
```bash
|
||||
# 員工到職
|
||||
POST /api/v1/emp-lifecycle/onboard
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"resume_id": 1,
|
||||
"keycloak_user_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"keycloak_username": "wang.ming",
|
||||
"hire_date": "2026-02-20",
|
||||
"departments": [
|
||||
{
|
||||
"department_id": 9,
|
||||
"position": "資深工程師",
|
||||
"membership_type": "permanent"
|
||||
},
|
||||
{
|
||||
"department_id": 12,
|
||||
"position": "專案經理",
|
||||
"membership_type": "project"
|
||||
}
|
||||
],
|
||||
"role_ids": [1, 2],
|
||||
"storage_quota_gb": 20,
|
||||
"email_quota_mb": 5120
|
||||
}
|
||||
```
|
||||
|
||||
**預期回應**:
|
||||
```json
|
||||
{
|
||||
"message": "Employee onboarded successfully",
|
||||
"employee": {
|
||||
"tenant_id": 1,
|
||||
"seq_no": 1,
|
||||
"tenant_emp_code": "PWD0001",
|
||||
"keycloak_user_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"keycloak_username": "wang.ming",
|
||||
"name": "王明",
|
||||
"hire_date": "2026-02-20"
|
||||
},
|
||||
"summary": {
|
||||
"departments_assigned": 2,
|
||||
"roles_assigned": 2,
|
||||
"services_enabled": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 查詢員工狀態
|
||||
|
||||
```bash
|
||||
# 查詢完整狀態
|
||||
GET /api/v1/emp-lifecycle/1/1/status
|
||||
|
||||
# 回應範例
|
||||
{
|
||||
"employee": {
|
||||
"tenant_id": 1,
|
||||
"seq_no": 1,
|
||||
"tenant_emp_code": "PWD0001",
|
||||
"name": "王明",
|
||||
"keycloak_user_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"keycloak_username": "wang.ming",
|
||||
"hire_date": "2026-02-20",
|
||||
"resign_date": null,
|
||||
"employment_status": "active",
|
||||
"storage_quota_gb": 20,
|
||||
"email_quota_mb": 5120
|
||||
},
|
||||
"departments": [
|
||||
{
|
||||
"department_id": 9,
|
||||
"department_name": "玄鐵風能",
|
||||
"position": "資深工程師",
|
||||
"membership_type": "permanent",
|
||||
"joined_at": "2026-02-20T10:00:00"
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"role_id": 1,
|
||||
"role_name": "HR管理員",
|
||||
"role_code": "HR_ADMIN",
|
||||
"assigned_at": "2026-02-20T10:00:00"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"service_id": 1,
|
||||
"service_name": "單一簽入",
|
||||
"service_code": "SSO",
|
||||
"quota_gb": null,
|
||||
"quota_mb": null,
|
||||
"enabled_at": "2026-02-20T10:00:00"
|
||||
},
|
||||
{
|
||||
"service_id": 4,
|
||||
"service_name": "網路硬碟",
|
||||
"service_code": "Drive",
|
||||
"quota_gb": 20,
|
||||
"quota_mb": null,
|
||||
"enabled_at": "2026-02-20T10:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 員工離職流程
|
||||
|
||||
```bash
|
||||
# 員工離職
|
||||
POST /api/v1/emp-lifecycle/1/1/offboard
|
||||
|
||||
# 回應範例
|
||||
{
|
||||
"message": "Employee offboarded successfully",
|
||||
"employee": {
|
||||
"tenant_emp_code": "PWD0001",
|
||||
"resign_date": "2026-02-20"
|
||||
},
|
||||
"summary": {
|
||||
"departments_removed": 2,
|
||||
"roles_revoked": 2,
|
||||
"services_disabled": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 資料庫驗證查詢
|
||||
|
||||
### 檢查員工任用設定
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
tenant_id,
|
||||
seq_no,
|
||||
tenant_emp_code,
|
||||
tenant_keycloak_user_id,
|
||||
tenant_keycloak_username,
|
||||
hire_at,
|
||||
storage_quota_gb,
|
||||
email_quota_mb,
|
||||
employment_status,
|
||||
is_active
|
||||
FROM tenant_emp_settings
|
||||
WHERE tenant_id = 1;
|
||||
```
|
||||
|
||||
### 檢查部門歸屬
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
dm.id,
|
||||
dm.employee_id,
|
||||
dm.department_id,
|
||||
d.name AS department_name,
|
||||
dm.position,
|
||||
dm.membership_type,
|
||||
dm.joined_at,
|
||||
dm.ended_at,
|
||||
dm.assigned_by,
|
||||
dm.is_active
|
||||
FROM tenant_dept_members dm
|
||||
LEFT JOIN tenant_departments d ON dm.department_id = d.id
|
||||
WHERE dm.tenant_id = 1
|
||||
ORDER BY dm.employee_id, dm.joined_at;
|
||||
```
|
||||
|
||||
### 檢查角色分配
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
ra.id,
|
||||
ra.keycloak_user_id,
|
||||
ra.role_id,
|
||||
r.role_code,
|
||||
r.role_name,
|
||||
ra.assigned_at,
|
||||
ra.revoked_at,
|
||||
ra.assigned_by,
|
||||
ra.is_active
|
||||
FROM tenant_user_role_assignments ra
|
||||
LEFT JOIN tenant_user_roles r ON ra.role_id = r.id
|
||||
WHERE ra.tenant_id = 1
|
||||
ORDER BY ra.keycloak_user_id, ra.assigned_at;
|
||||
```
|
||||
|
||||
### 檢查個人化服務
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
ss.id,
|
||||
ss.tenant_keycloak_user_id,
|
||||
ss.service_id,
|
||||
s.service_name,
|
||||
s.service_code,
|
||||
ss.quota_gb,
|
||||
ss.quota_mb,
|
||||
ss.enabled_at,
|
||||
ss.disabled_at,
|
||||
ss.enabled_by,
|
||||
ss.is_active
|
||||
FROM tenant_emp_personal_service_settings ss
|
||||
LEFT JOIN personal_services s ON ss.service_id = s.id
|
||||
WHERE ss.tenant_id = 1
|
||||
ORDER BY ss.tenant_keycloak_user_id, ss.service_id;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 測試步驟
|
||||
|
||||
### 前置準備
|
||||
|
||||
1. **確認資料庫狀態**:
|
||||
```bash
|
||||
cd q:/porscheworld_develop/hr-portal/backend
|
||||
python -m alembic current
|
||||
# 應顯示: 0012 (head)
|
||||
```
|
||||
|
||||
2. **啟動後端服務**:
|
||||
```bash
|
||||
cd q:/porscheworld_develop/hr-portal
|
||||
START_BACKEND.bat
|
||||
```
|
||||
|
||||
3. **確認服務運行**:
|
||||
```bash
|
||||
curl http://localhost:10181/api/v1/docs
|
||||
```
|
||||
|
||||
### 測試流程
|
||||
|
||||
#### Step 1: 建立測試用人員基本資料
|
||||
|
||||
```sql
|
||||
-- 手動建立測試資料 (或透過 API)
|
||||
INSERT INTO tenant_emp_resumes (tenant_id, name_tw, name_eng)
|
||||
VALUES (1, '王明', 'Ming Wang')
|
||||
RETURNING id;
|
||||
-- 假設回傳 id = 1
|
||||
```
|
||||
|
||||
#### Step 2: 執行到職流程
|
||||
|
||||
使用 Postman 或 curl:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:10181/api/v1/emp-lifecycle/onboard \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"resume_id": 1,
|
||||
"keycloak_user_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"keycloak_username": "wang.ming",
|
||||
"hire_date": "2026-02-20",
|
||||
"departments": [
|
||||
{"department_id": 1, "position": "工程師"}
|
||||
],
|
||||
"role_ids": [1],
|
||||
"storage_quota_gb": 20,
|
||||
"email_quota_mb": 5120
|
||||
}'
|
||||
```
|
||||
|
||||
#### Step 3: 驗證資料
|
||||
|
||||
```bash
|
||||
# 查詢狀態
|
||||
curl http://localhost:10181/api/v1/emp-lifecycle/1/1/status
|
||||
```
|
||||
|
||||
#### Step 4: 執行離職流程
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:10181/api/v1/emp-lifecycle/1/1/offboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 預期結果
|
||||
|
||||
### 到職後應建立的資料
|
||||
|
||||
1. ✅ `tenant_emp_settings`: 1 筆記錄
|
||||
2. ✅ `tenant_dept_members`: N 筆記錄(依 departments 數量)
|
||||
3. ✅ `tenant_user_role_assignments`: N 筆記錄(依 role_ids 數量)
|
||||
4. ✅ `tenant_emp_personal_service_settings`: 5 筆記錄(所有服務)
|
||||
|
||||
### 離職後應更新的資料
|
||||
|
||||
1. ✅ `tenant_emp_settings.employment_status` = 'resigned'
|
||||
2. ✅ `tenant_emp_settings.resign_date` = 當天日期
|
||||
3. ✅ `tenant_dept_members.is_active` = false, `ended_at` = 當前時間
|
||||
4. ✅ `tenant_user_role_assignments.is_active` = false, `revoked_at` = 當前時間
|
||||
5. ✅ `tenant_emp_personal_service_settings.is_active` = false, `disabled_at` = 當前時間
|
||||
|
||||
---
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 問題 1: Port 被占用
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
netstat -ano | findstr :10181
|
||||
taskkill /PID <PID> /F
|
||||
```
|
||||
|
||||
### 問題 2: 資料庫連線失敗
|
||||
|
||||
檢查連線字串:
|
||||
```
|
||||
postgresql://admin:DC1qaz2wsx@10.1.0.20:5433/hr_portal
|
||||
```
|
||||
|
||||
### 問題 3: Migration 版本不對
|
||||
|
||||
```bash
|
||||
cd q:/porscheworld_develop/hr-portal/backend
|
||||
python -m alembic upgrade head
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事項
|
||||
|
||||
⚠️ **重要**:
|
||||
- 此為測試環境,請勿在正式資料上測試
|
||||
- 測試前請備份資料庫
|
||||
- Keycloak User ID 必須是有效的 UUID 格式
|
||||
- 部門 ID 和角色 ID 必須在資料庫中存在
|
||||
|
||||
---
|
||||
|
||||
**測試文件版本**: v1.0
|
||||
**建立日期**: 2026-02-20
|
||||
**適用架構**: HR Portal v3.1 (多租戶 + 關聯表)
|
||||
Reference in New Issue
Block a user