"""伏笔管理数据模型 - 独立管理小说伏笔的埋入和回收""" from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Float, JSON, Boolean from sqlalchemy.sql import func from sqlalchemy.orm import relationship from app.database import Base import uuid class Foreshadow(Base): """ 伏笔管理表 - 独立管理小说伏笔 支持以下功能: 1. 从章节分析结果自动同步伏笔 2. 用户手动添加自定义伏笔 3. 关联埋入章节和计划回收章节 4. 长线伏笔管理 5. 章节生成时的伏笔提醒 """ __tablename__ = "foreshadows" id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) project_id = Column(String(36), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True) # === 伏笔内容 === title = Column(String(200), nullable=False, comment="伏笔标题") content = Column(Text, nullable=False, comment="伏笔详细内容/描述") hint_text = Column(Text, comment="埋伏笔时的暗示文本(原文摘录或概述)") resolution_text = Column(Text, comment="回收伏笔时的揭示文本(原文摘录或概述)") # === 来源信息 === source_type = Column(String(20), default='manual', comment="来源类型: analysis=分析提取, manual=手动添加") source_memory_id = Column(String(100), comment="来源记忆ID(如从分析结果同步)") source_analysis_id = Column(String(36), comment="来源分析任务ID") # === 章节关联 === # 埋入章节 plant_chapter_id = Column(String(36), ForeignKey("chapters.id", ondelete="SET NULL"), comment="埋入章节ID") plant_chapter_number = Column(Integer, comment="埋入章节号(冗余存储便于查询)") # 计划回收章节 target_resolve_chapter_id = Column(String(36), ForeignKey("chapters.id", ondelete="SET NULL"), comment="计划回收章节ID") target_resolve_chapter_number = Column(Integer, comment="计划回收章节号") # 实际回收章节 actual_resolve_chapter_id = Column(String(36), ForeignKey("chapters.id", ondelete="SET NULL"), comment="实际回收章节ID") actual_resolve_chapter_number = Column(Integer, comment="实际回收章节号") # === 状态管理 === status = Column(String(20), default='pending', index=True, comment=""" 伏笔状态: - pending: 待埋入(已规划但未写入章节) - planted: 已埋入(已在章节中埋下) - resolved: 已回收(已在章节中回收) - partially_resolved: 部分回收(长线伏笔可能分多次回收) - abandoned: 已废弃(决定不再使用此伏笔) """) is_long_term = Column(Boolean, default=False, comment="是否长线伏笔(跨多章的重要伏笔)") # === 重要性和优先级 === importance = Column(Float, default=0.5, comment="重要性评分 0.0-1.0") strength = Column(Integer, default=5, comment="伏笔强度 1-10(影响读者多强烈)") subtlety = Column(Integer, default=5, comment="隐藏度 1-10(越高越隐蔽)") urgency = Column(Integer, default=0, comment="紧急度: 0=不紧急, 1=需关注, 2=急需回收") # === 关联信息 === related_characters = Column(JSON, comment="关联角色名列表: ['角色1', '角色2']") related_foreshadow_ids = Column(JSON, comment="关联的其他伏笔ID列表(伏笔链)") tags = Column(JSON, comment="标签列表: ['身世', '悬念', '反转']") category = Column(String(50), comment="分类: identity(身世), mystery(悬念), item(物品), relationship(关系), event(事件)") # === 备注和说明 === notes = Column(Text, comment="创作备注(仅作者可见)") resolution_notes = Column(Text, comment="回收方式说明") # === AI辅助设置 === auto_remind = Column(Boolean, default=True, comment="是否在章节生成时自动提醒") remind_before_chapters = Column(Integer, default=5, comment="提前几章开始提醒回收") include_in_context = Column(Boolean, default=True, comment="是否包含在生成上下文中") # === 时间戳 === created_at = Column(DateTime, server_default=func.now(), comment="创建时间") updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="更新时间") planted_at = Column(DateTime, comment="埋入时间") resolved_at = Column(DateTime, comment="回收时间") def __repr__(self): return f"" def to_dict(self): """转换为字典格式""" return { "id": self.id, "project_id": self.project_id, "title": self.title, "content": self.content, "hint_text": self.hint_text, "resolution_text": self.resolution_text, "source_type": self.source_type, "source_memory_id": self.source_memory_id, "plant_chapter_id": self.plant_chapter_id, "plant_chapter_number": self.plant_chapter_number, "target_resolve_chapter_id": self.target_resolve_chapter_id, "target_resolve_chapter_number": self.target_resolve_chapter_number, "actual_resolve_chapter_id": self.actual_resolve_chapter_id, "actual_resolve_chapter_number": self.actual_resolve_chapter_number, "status": self.status, "is_long_term": self.is_long_term, "importance": self.importance, "strength": self.strength, "subtlety": self.subtlety, "urgency": self.urgency, "related_characters": self.related_characters or [], "related_foreshadow_ids": self.related_foreshadow_ids or [], "tags": self.tags or [], "category": self.category, "notes": self.notes, "resolution_notes": self.resolution_notes, "auto_remind": self.auto_remind, "remind_before_chapters": self.remind_before_chapters, "include_in_context": self.include_in_context, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, "planted_at": self.planted_at.isoformat() if self.planted_at else None, "resolved_at": self.resolved_at.isoformat() if self.resolved_at else None, } def to_context_string(self) -> str: """ 转换为上下文字符串(用于章节生成提示) """ parts = [] # 基本信息 parts.append(f"伏笔「{self.title}」") # 埋入信息 if self.plant_chapter_number: parts.append(f"(第{self.plant_chapter_number}章埋下)") # 内容摘要 content_preview = self.content[:100] if len(self.content) > 100 else self.content parts.append(f": {content_preview}") # 计划回收 if self.target_resolve_chapter_number: parts.append(f" [计划第{self.target_resolve_chapter_number}章回收]") # 关联角色 if self.related_characters: parts.append(f" 涉及: {', '.join(self.related_characters[:3])}") return "".join(parts) def get_urgency_level(self, current_chapter: int) -> int: """ 计算当前紧急度 Args: current_chapter: 当前章节号 Returns: 0=不紧急, 1=需关注, 2=急需回收, 3=已超期 """ if self.status != 'planted' or not self.target_resolve_chapter_number: return 0 chapters_remaining = self.target_resolve_chapter_number - current_chapter if chapters_remaining < 0: return 3 # 已超期 elif chapters_remaining <= 2: return 2 # 急需回收 elif chapters_remaining <= self.remind_before_chapters: return 1 # 需关注 else: return 0 # 不紧急