Files
hr-portal/test_onboarding.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

7.7 KiB

員工到職流程測試指南 (v3.1)

測試環境


API 端點清單

1. 個人化服務管理

# 取得所有可用服務
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. 員工到職流程 (完整)

# 員工到職
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
}

預期回應:

{
  "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. 查詢員工狀態

# 查詢完整狀態
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. 員工離職流程

# 員工離職
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
  }
}

資料庫驗證查詢

檢查員工任用設定

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;

檢查部門歸屬

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;

檢查角色分配

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;

檢查個人化服務

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. 確認資料庫狀態:

    cd q:/porscheworld_develop/hr-portal/backend
    python -m alembic current
    # 應顯示: 0012 (head)
    
  2. 啟動後端服務:

    cd q:/porscheworld_develop/hr-portal
    START_BACKEND.bat
    
  3. 確認服務運行:

    curl http://localhost:10181/api/v1/docs
    

測試流程

Step 1: 建立測試用人員基本資料

-- 手動建立測試資料 (或透過 API)
INSERT INTO tenant_emp_resumes (tenant_id, name_tw, name_eng)
VALUES (1, '王明', 'Ming Wang')
RETURNING id;
-- 假設回傳 id = 1

Step 2: 執行到職流程

使用 Postman 或 curl:

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: 驗證資料

# 查詢狀態
curl http://localhost:10181/api/v1/emp-lifecycle/1/1/status

Step 4: 執行離職流程

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 被占用

# Windows
netstat -ano | findstr :10181
taskkill /PID <PID> /F

問題 2: 資料庫連線失敗

檢查連線字串:

postgresql://admin:DC1qaz2wsx@10.1.0.20:5433/hr_portal

問題 3: Migration 版本不對

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 (多租戶 + 關聯表)