import uuid from datetime import datetime from sqlalchemy import String, Integer, Float, Text, DateTime, ForeignKey, JSON, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base def gen_uuid(): return str(uuid.uuid4()) class Book(Base): __tablename__ = "books" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) filename: Mapped[str] = mapped_column(String(500), nullable=False) title: Mapped[str | None] = mapped_column(String(500)) author: Mapped[str | None] = mapped_column(String(500)) language: Mapped[str] = mapped_column(String(20), default="zh") file_path: Mapped[str] = mapped_column(String(1000), nullable=False) file_hash: Mapped[str | None] = mapped_column(String(64), unique=True) char_count: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String(20), default="uploaded") created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) chapters: Mapped[list["Chapter"]] = relationship(back_populates="book", cascade="all, delete-orphan") class Chapter(Base): __tablename__ = "chapters" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) book_id: Mapped[str] = mapped_column(String(36), ForeignKey("books.id", ondelete="CASCADE")) order_idx: Mapped[int] = mapped_column(Integer, default=0) title: Mapped[str | None] = mapped_column(String(500)) level: Mapped[int] = mapped_column(Integer, default=1) heading_path: Mapped[str | None] = mapped_column(Text) cfi_start: Mapped[str | None] = mapped_column(String(500)) cfi_end: Mapped[str | None] = mapped_column(String(500)) char_count: Mapped[int] = mapped_column(Integer, default=0) book: Mapped["Book"] = relationship(back_populates="chapters") segments: Mapped[list["Segment"]] = relationship(back_populates="chapter", cascade="all, delete-orphan") class Segment(Base): __tablename__ = "segments" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=gen_uuid) chapter_id: Mapped[str] = mapped_column(String(36), ForeignKey("chapters.id", ondelete="CASCADE")) book_id: Mapped[str] = mapped_column(String(36), ForeignKey("books.id", ondelete="CASCADE")) order_idx: Mapped[int] = mapped_column(Integer, default=0) text: Mapped[str] = mapped_column(Text, nullable=False) char_count: Mapped[int] = mapped_column(Integer, default=0) cfi: Mapped[str | None] = mapped_column(String(500)) page_estimate: Mapped[float | None] = mapped_column(Float) metadata_: Mapped[str | None] = mapped_column("metadata", Text) embedding: Mapped[str | None] = mapped_column(Text) # JSON 浮点数组(向量检索用);NULL = 尚未生成向量 chapter: Mapped["Chapter"] = relationship(back_populates="segments")