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>
391 lines
7.7 KiB
Markdown
391 lines
7.7 KiB
Markdown
# 員工到職流程測試指南 (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 (多租戶 + 關聯表)
|