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>
This commit is contained in:
donald
2025-12-03 23:53:24 +08:00
commit db0f0bbfe7
50 changed files with 11883 additions and 0 deletions

90
app/models/interaction.py Normal file
View File

@@ -0,0 +1,90 @@
"""
讀者互動資料模型(訂閱、收藏、留言、筆記)
"""
from datetime import datetime
from sqlalchemy import String, Boolean, ForeignKey, Text, UniqueConstraint, Index
from sqlalchemy.orm import Mapped, mapped_column, relationship
from typing import Optional, List, TYPE_CHECKING
from app.db.session import Base
if TYPE_CHECKING:
from app.models.user import User
from app.models.group import Group
from app.models.report import Report
class Subscription(Base):
"""訂閱表"""
__tablename__ = "subscriptions"
__table_args__ = (
UniqueConstraint("user_id", "group_id", name="uk_user_group"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
group_id: Mapped[int] = mapped_column(ForeignKey("groups.id", ondelete="CASCADE"), nullable=False)
email_notify: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否Email通知")
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
# 關聯
user: Mapped["User"] = relationship(back_populates="subscriptions")
group: Mapped["Group"] = relationship(back_populates="subscriptions")
class Favorite(Base):
"""收藏表"""
__tablename__ = "favorites"
__table_args__ = (
UniqueConstraint("user_id", "report_id", name="uk_user_report"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
report_id: Mapped[int] = mapped_column(ForeignKey("reports.id", ondelete="CASCADE"), nullable=False)
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
# 關聯
user: Mapped["User"] = relationship(back_populates="favorites")
report: Mapped["Report"] = relationship(back_populates="favorites")
class Comment(Base):
"""留言表"""
__tablename__ = "comments"
__table_args__ = (
Index("idx_comments_report", "report_id"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
report_id: Mapped[int] = mapped_column(ForeignKey("reports.id", ondelete="CASCADE"), nullable=False)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False, comment="留言內容")
parent_id: Mapped[Optional[int]] = mapped_column(ForeignKey("comments.id"), comment="父留言ID")
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
# 關聯
report: Mapped["Report"] = relationship(back_populates="comments")
user: Mapped["User"] = relationship(back_populates="comments")
parent: Mapped[Optional["Comment"]] = relationship(remote_side=[id], backref="replies")
class Note(Base):
"""個人筆記表"""
__tablename__ = "notes"
__table_args__ = (
Index("idx_notes_user_report", "user_id", "report_id"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
report_id: Mapped[int] = mapped_column(ForeignKey("reports.id", ondelete="CASCADE"), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False, comment="筆記內容")
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
# 關聯
user: Mapped["User"] = relationship(back_populates="notes")
report: Mapped["Report"] = relationship(back_populates="notes")