# Phase F.1:数据模型升级 日期:2026-04-04 状态:待开始 依赖:F.0(已完成) 前置:models/forum.py --- ## 1. 本阶段目的 升级 Jarvis Forum 的数据模型,支持: - 多板块系统(指令/讨论/任务等) - 标签系统(细粒度分类) - 帖子元数据扩展(阅读量、点赞、收藏等) - 层级回复结构 --- ## 2. 目标模型 ### 2.1 ForumBoard(板块) ```python class ForumBoard(BaseModel): __tablename__ = "forum_boards" id: str = Field(default_factory=lambda: str(uuid.uuid4())) name: str # 板块名称,如 "指令广场" slug: str # URL 友好名称,如 "instruction" description: Optional[str] icon: Optional[str] # emoji 或图标名 sort_order: int = 0 is_active: bool = True min_role: str = "user" # 查看权限:guest/user/moderator/admin parent_id: Optional[str] # 父板块,支持层级 color: Optional[str] # 板块颜色 ``` ### 2.2 ForumPost(帖子)扩展 ```python class ForumPost(BaseModel): __tablename__ = "forum_posts" # === 现有字段 === user_id: str title: str content: str reply_count: int = 0 # === 新增字段 === board_id: Optional[str] = None # 所属板块 parent_id: Optional[str] = None # 父帖子(支持回复嵌套) # 分类与标签 category: Optional[str] # instruction/discussion/question/praise/bug tags: Optional[str] = "" # JSON 数组 ["python", "api"] # 状态与权限 is_pinned: bool = False # 置顶 is_locked: bool = False # 锁定(禁止回复) is_deleted: bool = False # 软删除 is_executed: bool = False # 指令已执行 execution_result: Optional[str] # 统计与互动 view_count: int = 0 like_count: int = 0 favorite_count: int = 0 share_count: int = 0 # 时间戳 last_reply_at: Optional[datetime] = None # 最后回复时间 last_activity_at: Optional[datetime] = None # === 关系 === board = relationship("ForumBoard", back_populates="posts") replies = relationship("ForumReply", back_populates="post", cascade="all, delete-orphan") post_tags = relationship("ForumPostTag", back_populates="post", cascade="all, delete-orphan") likes = relationship("ForumLike", back_populates="post", cascade="all, delete-orphan") ``` ### 2.3 ForumReply(回复)扩展 ```python class ForumReply(BaseModel): __tablename__ = "forum_replies" # === 现有字段 === post_id: str user_id: Optional[str] agent_id: Optional[str] content: str is_ai_reply: bool = False # === 新增字段 === parent_id: Optional[str] = None # 父回复(支持嵌套) floor: int = 1 # 楼层号 is_pinned: bool = False # 置顶 is_best: bool = False # 最佳回复 is_deleted: bool = False # 软删除 like_count: int = 0 edited_at: Optional[datetime] = None # 编辑时间 # === 关系 === post = relationship("ForumPost", back_populates="replies") parent = relationship("ForumReply", remote_side=[id], backref="children") likes = relationship("ForumLike", back_populates="reply", cascade="all, delete-orphan") ``` ### 2.4 ForumTag(标签) ```python class ForumTag(BaseModel): __tablename__ = "forum_tags" id: str = Field(default_factory=lambda: str(uuid.uuid4())) name: str # 标签名,如 "python" slug: str # URL 友好名 color: str = "#666666" # 标签颜色 post_count: int = 0 # 帖子数量(缓存) is_official: bool = False # 官方标签 board_id: Optional[str] = None # 限定板块 posts = relationship("ForumPostTag", back_populates="tag") class ForumPostTag(BaseModel): """帖子-标签关联""" __tablename__ = "forum_post_tags" post_id: str tag_id: str post = relationship("ForumPost", back_populates="post_tags") tag = relationship("ForumTag", back_populates="posts") ``` ### 2.5 ForumLike(点赞) ```python class ForumLike(BaseModel): __tablename__ = "forum_likes" user_id: str post_id: Optional[str] = None reply_id: Optional[str] = None created_at: datetime = Field(default_factory=datetime.utcnow) post = relationship("ForumPost", back_populates="likes") reply = relationship("ForumReply", back_populates="likes") ``` ### 2.6 ForumStats(用户统计) ```python class ForumStats(BaseModel): """用户论坛统计""" __tablename__ = "forum_stats" user_id: str = Field(primary_key=True) post_count: int = 0 reply_count: int = 0 like_received: int = 0 # 收到的赞 best_reply_count: int = 0 # 最佳回复数 score: int = 0 # 积分 last_post_at: Optional[datetime] = None last_reply_at: Optional[datetime] = None ``` --- ## 3. 数据库迁移 ### 3.1 新增表 ```sql -- 板块表 CREATE TABLE forum_boards ( id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT UNIQUE NOT NULL, description TEXT, icon TEXT, sort_order INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT TRUE, min_role TEXT DEFAULT 'user', parent_id TEXT, color TEXT ); -- 帖子-标签关联表 CREATE TABLE forum_post_tags ( post_id TEXT NOT NULL, tag_id TEXT NOT NULL, PRIMARY KEY (post_id, tag_id) ); -- 标签表 CREATE TABLE forum_tags ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, slug TEXT UNIQUE NOT NULL, color TEXT DEFAULT '#666666', post_count INTEGER DEFAULT 0, is_official BOOLEAN DEFAULT FALSE, board_id TEXT ); -- 点赞表 CREATE TABLE forum_likes ( user_id TEXT NOT NULL, post_id TEXT, reply_id TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, post_id, reply_id) ); -- 用户论坛统计表 CREATE TABLE forum_stats ( user_id TEXT PRIMARY KEY, post_count INTEGER DEFAULT 0, reply_count INTEGER DEFAULT 0, like_received INTEGER DEFAULT 0, best_reply_count INTEGER DEFAULT 0, score INTEGER DEFAULT 0, last_post_at TIMESTAMP, last_reply_at TIMESTAMP ); -- 扩展 forum_posts 表 ALTER TABLE forum_posts ADD COLUMN board_id TEXT; ALTER TABLE forum_posts ADD COLUMN parent_id TEXT; ALTER TABLE forum_posts ADD COLUMN tags TEXT DEFAULT ''; ALTER TABLE forum_posts ADD COLUMN is_pinned BOOLEAN DEFAULT FALSE; ALTER TABLE forum_posts ADD COLUMN is_locked BOOLEAN DEFAULT FALSE; ALTER TABLE forum_posts ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE; ALTER TABLE forum_posts ADD COLUMN view_count INTEGER DEFAULT 0; ALTER TABLE forum_posts ADD COLUMN like_count INTEGER DEFAULT 0; ALTER TABLE forum_posts ADD COLUMN favorite_count INTEGER DEFAULT 0; ALTER TABLE forum_posts ADD COLUMN share_count INTEGER DEFAULT 0; ALTER TABLE forum_posts ADD COLUMN last_reply_at TIMESTAMP; ALTER TABLE forum_posts ADD COLUMN last_activity_at TIMESTAMP; -- 扩展 forum_replies 表 ALTER TABLE forum_replies ADD COLUMN parent_id TEXT; ALTER TABLE forum_replies ADD COLUMN floor INTEGER DEFAULT 1; ALTER TABLE forum_replies ADD COLUMN is_pinned BOOLEAN DEFAULT FALSE; ALTER TABLE forum_replies ADD COLUMN is_best BOOLEAN DEFAULT FALSE; ALTER TABLE forum_replies ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE; ALTER TABLE forum_replies ADD COLUMN like_count INTEGER DEFAULT 0; ALTER TABLE forum_replies ADD COLUMN edited_at TIMESTAMP; ``` --- ## 4. Schema 扩展 ### 4.1 ForumBoardSchema ```python class ForumBoardCreate(BaseModel): name: str = Field(..., max_length=100) slug: str = Field(..., max_length=50, pattern=r"^[a-z0-9-]+$") description: Optional[str] = None icon: Optional[str] = None sort_order: int = 0 min_role: str = "user" parent_id: Optional[str] = None color: Optional[str] = None class ForumBoardOut(BaseModel): id: str name: str slug: str description: Optional[str] icon: Optional[str] sort_order: int post_count: int = 0 # 动态计算 class ForumPostCreate(BaseModel): title: str = Field(..., max_length=200) content: str = Field(..., max_length=50000) board_id: Optional[str] = None category: Optional[str] = None tags: List[str] = [] class ForumPostOut(BaseModel): id: str user_id: str title: str content: str board_id: Optional[str] board_name: Optional[str] = None category: Optional[str] tags: List[str] = [] reply_count: int view_count: int like_count: int is_pinned: bool is_locked: bool created_at: datetime last_reply_at: Optional[datetime] user_liked: bool = False # 当前用户是否点赞 ``` --- ## 5. 实现步骤 | 步骤 | 任务 | 优先级 | |------|------|--------| | 1 | 创建数据库迁移脚本 | 🟢 高 | | 2 | 扩展 models/forum.py | 🟢 高 | | 3 | 创建 ForumBoard 模型 | 🟢 高 | | 4 | 创建 ForumTag/ForumPostTag 模型 | 🟡 中 | | 5 | 创建 ForumLike 模型 | 🟡 中 | | 6 | 创建 ForumStats 模型 | 🟡 中 | | 7 | 扩展 schemas/forum.py | 🟢 高 | | 8 | 编写单元测试 | 🟡 中 | --- ## 6. 核心文件变更 | 文件 | 变更 | |------|------| | `models/forum.py` | 新增 ForumBoard, ForumTag, ForumPostTag, ForumLike, ForumStats;扩展 ForumPost, ForumReply | | `schemas/forum.py` | 新增 ForumBoardCreate/Out;扩展 ForumPostCreate/Out | | `database.py` | 注册新模型 | --- ## 7. 工作量估算 | 任务 | 工作量 | |------|--------| | 数据库迁移脚本 | 0.5 天 | | 模型扩展 | 0.5 天 | | Schema 扩展 | 0.5 天 | | 单元测试 | 0.5 天 | | **总计** | **2 天** | --- ## 8. 验收标准 - [ ] ForumBoard 模型可创建/查询板块 - [ ] ForumPost 可关联板块和标签 - [ ] ForumTag 可创建/查询标签 - [ ] ForumLike 支持点赞功能 - [ ] ForumStats 正确统计用户数据 - [ ] 所有新模型有对应的 Pydantic Schema - [ ] 迁移脚本可回滚 - [ ] 单元测试覆盖新增功能