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>
89 lines
2.6 KiB
Python
89 lines
2.6 KiB
Python
"""
|
|
測試租戶內序號自動生成
|
|
展示每個租戶的資料都從 1 開始編號
|
|
"""
|
|
from app.db.session import get_db
|
|
from app.models.employee import Employee
|
|
from app.models.tenant import Tenant
|
|
from sqlalchemy import text
|
|
|
|
def test_tenant_sequences():
|
|
db = next(get_db())
|
|
|
|
print("=" * 80)
|
|
print("租戶內序號測試")
|
|
print("=" * 80)
|
|
|
|
# 1. 查看現有資料的序號分佈
|
|
print("\n【現有員工資料】")
|
|
employees = db.query(Employee).order_by(Employee.tenant_id, Employee.seq_no).all()
|
|
|
|
current_tenant = None
|
|
for emp in employees:
|
|
if emp.tenant_id != current_tenant:
|
|
current_tenant = emp.tenant_id
|
|
tenant = db.query(Tenant).filter(Tenant.id == emp.tenant_id).first()
|
|
print(f"\n租戶 {emp.tenant_id} ({tenant.tenant_code if tenant else 'N/A'}):")
|
|
|
|
print(f" seq_no={emp.seq_no:3d} | employee_id={emp.employee_id:8s} | {emp.legal_name}")
|
|
|
|
# 2. 測試新增員工時序號自動生成
|
|
print("\n" + "=" * 80)
|
|
print("【測試新增員工 - 序號自動生成】")
|
|
print("=" * 80)
|
|
|
|
# 查詢租戶 1 的最大序號
|
|
max_seq = db.query(text("MAX(seq_no)")).select_from(Employee).filter(
|
|
Employee.tenant_id == 1
|
|
).scalar() or 0
|
|
|
|
print(f"\n租戶 1 當前最大序號: {max_seq}")
|
|
print(f"預期下一個序號: {max_seq + 1}")
|
|
|
|
# 3. 展示跨租戶序號獨立性
|
|
print("\n" + "=" * 80)
|
|
print("【租戶序號獨立性】")
|
|
print("=" * 80)
|
|
|
|
result = db.execute(text("""
|
|
SELECT
|
|
tenant_id,
|
|
COUNT(*) as total,
|
|
MIN(seq_no) as min_seq,
|
|
MAX(seq_no) as max_seq
|
|
FROM org_employees
|
|
GROUP BY tenant_id
|
|
ORDER BY tenant_id
|
|
"""))
|
|
|
|
print("\n租戶ID | 員工數 | 最小序號 | 最大序號")
|
|
print("-" * 50)
|
|
for row in result:
|
|
print(f"{row.tenant_id:7d} | {row.total:6d} | {row.min_seq:8d} | {row.max_seq:8d}")
|
|
|
|
# 4. 展示觸發器運作
|
|
print("\n" + "=" * 80)
|
|
print("【觸發器狀態】")
|
|
print("=" * 80)
|
|
|
|
triggers = db.execute(text("""
|
|
SELECT
|
|
trigger_name,
|
|
event_manipulation,
|
|
event_object_table
|
|
FROM information_schema.triggers
|
|
WHERE trigger_schema = 'public'
|
|
AND trigger_name LIKE '%seq_no%'
|
|
ORDER BY event_object_table
|
|
"""))
|
|
|
|
print("\n觸發器名稱".ljust(40), "事件".ljust(10), "目標表")
|
|
print("-" * 80)
|
|
for row in triggers:
|
|
print(f"{row.trigger_name:40s} {row.event_manipulation:10s} {row.event_object_table}")
|
|
|
|
db.close()
|
|
|
|
if __name__ == "__main__":
|
|
test_tenant_sequences()
|