Files
hr-portal/backend/alembic/versions/0011_cleanup_and_finalize.py
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

116 lines
3.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""cleanup and finalize
Revision ID: 0011
Revises: 0010
Create Date: 2026-02-20
架構收尾與清理:
1. 移除廢棄表business_units, employee_identities
2. 為 tenant_role_rights 新增 is_active
3. 重新命名觸發器org_* → tenant_*
"""
from alembic import op
import sqlalchemy as sa
revision = '0011'
down_revision = '0010'
branch_labels = None
depends_on = None
def upgrade() -> None:
# =========================================================
# Phase 1: 移除廢棄表(先移除外鍵約束)
# =========================================================
# 1.1 先移除依賴 business_units 的外鍵
# employee_identities.business_unit_id FK
op.drop_constraint('employee_identities_business_unit_id_fkey', 'employee_identities', type_='foreignkey')
# tenant_email_accounts.business_unit_id FK如果存在
try:
op.drop_constraint('fk_email_accounts_business_unit', 'tenant_email_accounts', type_='foreignkey')
except:
pass # 可能不存在
# 1.2 移除 employee_identities 表(已被 tenant_emp_setting 取代)
op.drop_table('employee_identities')
# 1.3 移除 business_units 表(已被 tenant_departments 取代)
op.drop_table('business_units')
# =========================================================
# Phase 2: 為 tenant_role_rights 新增 is_active
# =========================================================
op.add_column('tenant_role_rights', sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true', comment='是否啟用'))
# =========================================================
# Phase 3: 重新命名觸發器org_* → tenant_*
# =========================================================
# 3.1 tenant_departments
op.execute("""
DROP TRIGGER IF EXISTS trigger_org_departments_seq_no ON tenant_departments;
CREATE TRIGGER trigger_tenant_departments_seq_no
BEFORE INSERT ON tenant_departments
FOR EACH ROW
EXECUTE FUNCTION generate_tenant_seq_no();
""")
# 3.2 tenant_employees
op.execute("""
DROP TRIGGER IF EXISTS trigger_org_employees_seq_no ON tenant_employees;
CREATE TRIGGER trigger_tenant_employees_seq_no
BEFORE INSERT ON tenant_employees
FOR EACH ROW
EXECUTE FUNCTION generate_tenant_seq_no();
""")
# 3.3 tenant_user_roles
op.execute("""
DROP TRIGGER IF EXISTS trigger_org_user_roles_seq_no ON tenant_user_roles;
CREATE TRIGGER trigger_tenant_user_roles_seq_no
BEFORE INSERT ON tenant_user_roles
FOR EACH ROW
EXECUTE FUNCTION generate_tenant_seq_no();
""")
def downgrade() -> None:
# 恢復觸發器名稱
op.execute("DROP TRIGGER IF EXISTS trigger_tenant_user_roles_seq_no ON tenant_user_roles")
op.execute("""
CREATE TRIGGER trigger_org_user_roles_seq_no
BEFORE INSERT ON tenant_user_roles
FOR EACH ROW
EXECUTE FUNCTION generate_tenant_seq_no()
""")
op.execute("DROP TRIGGER IF EXISTS trigger_tenant_employees_seq_no ON tenant_employees")
op.execute("""
CREATE TRIGGER trigger_org_employees_seq_no
BEFORE INSERT ON tenant_employees
FOR EACH ROW
EXECUTE FUNCTION generate_tenant_seq_no()
""")
op.execute("DROP TRIGGER IF EXISTS trigger_tenant_departments_seq_no ON tenant_departments")
op.execute("""
CREATE TRIGGER trigger_org_departments_seq_no
BEFORE INSERT ON tenant_departments
FOR EACH ROW
EXECUTE FUNCTION generate_tenant_seq_no()
""")
# 移除 is_active
op.drop_column('tenant_role_rights', 'is_active')
# 恢復廢棄表不實現downgrade 不支援重建複雜資料)
# op.create_table('employee_identities', ...)
# op.create_table('business_units', ...)