Files
daily-news-app/app/models/system.py
donald db0f0bbfe7 Initial commit: Daily News App
企業內部新聞彙整與分析系統
- 自動新聞抓取 (Digitimes, 經濟日報, 工商時報)
- AI 智慧摘要 (OpenAI/Claude/Ollama)
- 群組管理與訂閱通知
- 已清理 Python 快取檔案

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 23:53:24 +08:00

104 lines
4.2 KiB
Python

"""
系統設定與日誌資料模型
"""
from datetime import datetime
from sqlalchemy import String, ForeignKey, Text, JSON, Enum as SQLEnum, Index
from sqlalchemy.orm import Mapped, mapped_column, relationship
from typing import Optional, TYPE_CHECKING
import enum
from app.db.session import Base
if TYPE_CHECKING:
from app.models.user import User
from app.models.report import Report
class SettingType(str, enum.Enum):
"""設定值類型"""
STRING = "string"
NUMBER = "number"
BOOLEAN = "boolean"
JSON = "json"
class NotificationType(str, enum.Enum):
"""通知類型"""
EMAIL = "email"
SYSTEM = "system"
class NotificationStatus(str, enum.Enum):
"""通知狀態"""
PENDING = "pending"
SENT = "sent"
FAILED = "failed"
class SystemSetting(Base):
"""系統設定表"""
__tablename__ = "system_settings"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
setting_key: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, comment="設定鍵")
setting_value: Mapped[Optional[str]] = mapped_column(Text, comment="設定值")
setting_type: Mapped[SettingType] = mapped_column(SQLEnum(SettingType), default=SettingType.STRING)
description: Mapped[Optional[str]] = mapped_column(String(200), comment="設定描述")
updated_by: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"), comment="更新者ID")
updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
def get_value(self):
"""取得轉換後的設定值"""
if self.setting_value is None:
return None
if self.setting_type == SettingType.NUMBER:
return float(self.setting_value) if '.' in self.setting_value else int(self.setting_value)
if self.setting_type == SettingType.BOOLEAN:
return self.setting_value.lower() in ('true', '1', 'yes')
if self.setting_type == SettingType.JSON:
import json
return json.loads(self.setting_value)
return self.setting_value
class AuditLog(Base):
"""操作日誌表"""
__tablename__ = "audit_logs"
__table_args__ = (
Index("idx_audit_user", "user_id"),
Index("idx_audit_action", "action"),
Index("idx_audit_created", "created_at"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"), comment="操作用戶ID")
action: Mapped[str] = mapped_column(String(50), nullable=False, comment="操作類型")
target_type: Mapped[Optional[str]] = mapped_column(String(50), comment="目標類型")
target_id: Mapped[Optional[str]] = mapped_column(String(50), comment="目標ID")
details: Mapped[Optional[dict]] = mapped_column(JSON, comment="操作詳情")
ip_address: Mapped[Optional[str]] = mapped_column(String(45), comment="IP地址")
user_agent: Mapped[Optional[str]] = mapped_column(String(500), comment="User Agent")
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
class NotificationLog(Base):
"""通知記錄表"""
__tablename__ = "notification_logs"
__table_args__ = (
Index("idx_notification_status", "status"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False)
report_id: Mapped[Optional[int]] = mapped_column(ForeignKey("reports.id"), comment="關聯報告ID")
notification_type: Mapped[NotificationType] = mapped_column(SQLEnum(NotificationType), default=NotificationType.EMAIL)
subject: Mapped[Optional[str]] = mapped_column(String(200), comment="通知標題")
content: Mapped[Optional[str]] = mapped_column(Text, comment="通知內容")
status: Mapped[NotificationStatus] = mapped_column(SQLEnum(NotificationStatus), default=NotificationStatus.PENDING)
sent_at: Mapped[Optional[datetime]] = mapped_column()
error_message: Mapped[Optional[str]] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
# 關聯
report: Mapped[Optional["Report"]] = relationship(back_populates="notifications")