Files
daily-news-app/app/models/group.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

83 lines
3.7 KiB
Python

"""
群組與關鍵字資料模型
"""
from datetime import datetime
from sqlalchemy import String, Boolean, ForeignKey, Text, JSON, Enum as SQLEnum, UniqueConstraint, Index, DECIMAL
from sqlalchemy.orm import Mapped, mapped_column, relationship
from typing import Optional, List
import enum
from app.db.session import Base
class GroupCategory(str, enum.Enum):
"""群組分類"""
INDUSTRY = "industry"
TOPIC = "topic"
class Group(Base):
"""群組表"""
__tablename__ = "groups"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(100), nullable=False, comment="群組名稱")
description: Mapped[Optional[str]] = mapped_column(Text, comment="群組描述")
category: Mapped[GroupCategory] = mapped_column(SQLEnum(GroupCategory), nullable=False, comment="分類")
ai_background: Mapped[Optional[str]] = mapped_column(Text, comment="AI背景資訊設定")
ai_prompt: Mapped[Optional[str]] = mapped_column(Text, comment="AI摘要方向提示")
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否啟用")
created_by: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id"), comment="建立者ID")
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
# 關聯
keywords: Mapped[List["Keyword"]] = relationship(back_populates="group", cascade="all, delete-orphan")
article_matches: Mapped[List["ArticleGroupMatch"]] = relationship(back_populates="group", cascade="all, delete-orphan")
reports: Mapped[List["Report"]] = relationship(back_populates="group")
subscriptions: Mapped[List["Subscription"]] = relationship(back_populates="group", cascade="all, delete-orphan")
class Keyword(Base):
"""關鍵字表"""
__tablename__ = "keywords"
__table_args__ = (
UniqueConstraint("group_id", "keyword", name="uk_group_keyword"),
Index("idx_keywords_keyword", "keyword"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
group_id: Mapped[int] = mapped_column(ForeignKey("groups.id", ondelete="CASCADE"), nullable=False, comment="所屬群組ID")
keyword: Mapped[str] = mapped_column(String(100), nullable=False, comment="關鍵字")
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
# 關聯
group: Mapped["Group"] = relationship(back_populates="keywords")
class ArticleGroupMatch(Base):
"""新聞-群組匹配關聯表"""
__tablename__ = "article_group_matches"
__table_args__ = (
UniqueConstraint("article_id", "group_id", name="uk_article_group"),
Index("idx_matches_group", "group_id"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
article_id: Mapped[int] = mapped_column(ForeignKey("news_articles.id", ondelete="CASCADE"), nullable=False)
group_id: Mapped[int] = mapped_column(ForeignKey("groups.id", ondelete="CASCADE"), nullable=False)
matched_keywords: Mapped[Optional[list]] = mapped_column(JSON, comment="匹配到的關鍵字列表")
match_score: Mapped[Optional[float]] = mapped_column(DECIMAL(5, 2), comment="匹配分數")
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
# 關聯
article: Mapped["NewsArticle"] = relationship(back_populates="group_matches")
group: Mapped["Group"] = relationship(back_populates="article_matches")
# 避免循環引入
from app.models.news import NewsArticle
from app.models.report import Report
from app.models.interaction import Subscription