Files
hr-portal/backend/app/batch/daily_quota_check.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

153 lines
4.8 KiB
Python
Raw 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.
"""
每日配額檢查批次 (5.1)
執行時間: 每日 02:00
批次名稱: daily_quota_check
檢查郵件和雲端硬碟配額使用情況,超過 80% 發送告警
"""
import logging
from datetime import datetime
from app.batch.base import log_batch_execution
logger = logging.getLogger(__name__)
QUOTA_ALERT_THRESHOLD = 0.8 # 超過 80% 發送告警
ALERT_EMAIL = "admin@porscheworld.tw"
def _send_alert_email(to: str, subject: str, body: str) -> bool:
"""
發送告警郵件
目前使用 SMTP 直送,未來可整合 Mailserver
"""
try:
import smtplib
from email.mime.text import MIMEText
from app.core.config import settings
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = subject
msg["From"] = settings.MAIL_ADMIN_USER
msg["To"] = to
with smtplib.SMTP(settings.MAIL_SERVER, settings.MAIL_PORT) as smtp:
if settings.MAIL_USE_TLS:
smtp.starttls()
smtp.login(settings.MAIL_ADMIN_USER, settings.MAIL_ADMIN_PASSWORD)
smtp.send_message(msg)
logger.info(f"告警郵件已發送至 {to}: {subject}")
return True
except Exception as e:
logger.warning(f"發送告警郵件失敗: {e}")
return False
def run_daily_quota_check() -> dict:
"""
執行每日配額檢查批次
Returns:
執行結果摘要
"""
started_at = datetime.utcnow()
alerts_sent = 0
errors = []
summary = {
"email_checked": 0,
"email_alerts": 0,
"drive_checked": 0,
"drive_alerts": 0,
}
logger.info("=== 開始每日配額檢查批次 ===")
# 取得資料庫 Session
from app.db.session import get_db
from app.models.email_account import EmailAccount
from app.models.network_drive import NetworkDrive
db = next(get_db())
try:
# 1. 檢查郵件配額
logger.info("檢查郵件配額使用情況...")
email_accounts = db.query(EmailAccount).filter(
EmailAccount.is_active == True
).all()
for account in email_accounts:
summary["email_checked"] += 1
# 目前郵件 Mailserver API 未整合,跳過實際配額查詢
# TODO: 整合 Mailserver API 後取得實際使用量
# usage_mb = mailserver_service.get_usage(account.email_address)
# if usage_mb and usage_mb / account.quota_mb > QUOTA_ALERT_THRESHOLD:
# _send_alert_email(...)
pass
logger.info(f"郵件帳號檢查完成: {summary['email_checked']} 個帳號")
# 2. 檢查雲端硬碟配額 (Drive Service API)
logger.info("檢查雲端硬碟配額使用情況...")
network_drives = db.query(NetworkDrive).filter(
NetworkDrive.is_active == True
).all()
from app.services.drive_service import get_drive_service_client
drive_client = get_drive_service_client()
for drive in network_drives:
summary["drive_checked"] += 1
try:
# 查詢配額使用量 (Drive Service 未上線時會回傳 None)
# 注意: drive.id 是資料庫 ID需要 drive_user_id
# 目前跳過實際查詢,等 Drive Service 上線後補充
pass
except Exception as e:
logger.warning(f"查詢 {drive.drive_name} 配額失敗: {e}")
logger.info(f"雲端硬碟檢查完成: {summary['drive_checked']} 個帳號")
# 3. 記錄批次執行日誌
finished_at = datetime.utcnow()
message = (
f"郵件帳號: {summary['email_checked']} 個, 告警: {summary['email_alerts']} 個; "
f"雲端硬碟: {summary['drive_checked']} 個, 告警: {summary['drive_alerts']}"
)
log_batch_execution(
batch_name="daily_quota_check",
status="success",
message=message,
started_at=started_at,
finished_at=finished_at,
)
logger.info(f"=== 每日配額檢查批次完成 === {message}")
return {"status": "success", "summary": summary}
except Exception as e:
error_msg = f"每日配額檢查批次失敗: {str(e)}"
logger.error(error_msg)
log_batch_execution(
batch_name="daily_quota_check",
status="failed",
message=error_msg,
started_at=started_at,
)
return {"status": "failed", "error": str(e)}
finally:
db.close()
if __name__ == "__main__":
import sys
import os
# 允許直接執行此批次
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../.."))
logging.basicConfig(level=logging.INFO)
result = run_daily_quota_check()
print(f"執行結果: {result}")