362 lines
9.7 KiB
Markdown
362 lines
9.7 KiB
Markdown
# 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
|
||
- [ ] 迁移脚本可回滚
|
||
- [ ] 单元测试覆盖新增功能
|