9.7 KiB
9.7 KiB
Phase F.1:数据模型升级
日期:2026-04-04 状态:待开始 依赖:F.0(已完成) 前置:models/forum.py
1. 本阶段目的
升级 Jarvis Forum 的数据模型,支持:
- 多板块系统(指令/讨论/任务等)
- 标签系统(细粒度分类)
- 帖子元数据扩展(阅读量、点赞、收藏等)
- 层级回复结构
2. 目标模型
2.1 ForumBoard(板块)
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(帖子)扩展
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(回复)扩展
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(标签)
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(点赞)
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(用户统计)
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 新增表
-- 板块表
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
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
- 迁移脚本可回滚
- 单元测试覆盖新增功能