大纲系统
以树状结构组织小说的整体框架和情节脉络
📖 系统概述
大纲系统采用树状结构管理小说的整体框架,支持卷、章、小节三层层级。通过 AI 辅助,用户可以快速生成完整大纲或续写后续情节,并通过拖拽交互自由调整结构。
🎯 功能特性
1. 三层树状结构
项目 (NovelProject)
│
├── 卷 (Volume) - 第一层
│ │
│ ├── 章 (Chapter) - 第二层
│ │ │
│ │ ├── 小节 (Section) - 第三层
│ │ └── 小节 (Section)
│ │
│ └── 章 (Chapter)
│
└── 卷 (Volume)| 层级 | 节点类型 | 说明 |
|---|---|---|
| 第一层 | volume | 卷,最高层级容器 |
| 第二层 | chapter | 章,卷的子节点 |
| 第三层 | section | 小节,章的子节点 |
2. AI 大纲生成
根据项目设定(标题、类型、风格、简介)一键生成完整大纲结构:
输入: 项目基本信息 + 生成参数
↓
AI 分析: 理解故事类型和风格
↓
输出: 完整的卷/章/节结构生成参数:
- 卷数量
- 每卷章节数
- 情节复杂度
- 是否包含描述
3. AI 大纲续写
基于现有大纲内容,智能续写后续情节发展:
输入: 现有大纲 + 续写位置
↓
AI 分析: 理解情节走向和角色发展
↓
输出: 后续章节大纲4. 拖拽排序
- 同级拖拽: 调整同一父节点下的顺序
- 跨级拖拽: 将节点移动到不同父节点
- 层级限制: 自动限制最大三层深度
🗄️ 数据模型
OutlineNode 模型
python
class OutlineNode(models.Model):
id = fields.IntField(pk=True)
project_id = fields.IntField() # 所属项目
parent_id = fields.IntField(null=True) # 父节点ID(null=根节点)
node_type = fields.CharField(max_length=20) # volume/chapter/section
title = fields.CharField(max_length=200) # 节点标题
description = fields.TextField(null=True) # 节点描述/大纲内容
position = fields.IntField(default=0) # 同级排序位置
is_expanded = fields.BooleanField(default=True) # UI展开状态
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
class Meta:
table = "outline_nodes"树状结构关系
OutlineNode (parent_id = null) ← 根节点(卷)
│
└── OutlineNode (parent_id = 根节点ID) ← 子节点(章)
│
└── OutlineNode (parent_id = 章ID) ← 孙节点(小节)与章节的关联
每个大纲节点可以关联一个章节(Chapter),用于将大纲与实际内容对应:
OutlineNode (chapter类型)
│
└──→ Chapter (outline_node_id = OutlineNode.id)🔌 API 接口
基础路径
/api/outline接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /project/{project_id} | 获取项目完整大纲树 |
POST | / | 创建大纲节点 |
PUT | /{node_id} | 更新节点信息 |
DELETE | /{node_id} | 删除节点(含子节点) |
PUT | /{node_id}/move | 移动节点位置 |
POST | /generate | AI 生成大纲 |
POST | /continue | AI 续写大纲 |
请求/响应示例
获取大纲树
请求:
GET /api/outline/project/1响应:
json
{
"nodes": [
{
"id": 1,
"node_type": "volume",
"title": "第一卷 起始",
"description": "故事的开端...",
"position": 0,
"is_expanded": true,
"children": [
{
"id": 2,
"node_type": "chapter",
"title": "第一章 初遇",
"description": "主角与女主的相遇...",
"position": 0,
"children": [...]
}
]
}
]
}AI 生成大纲
请求:
json
POST /api/outline/generate
{
"project_id": 1,
"volume_count": 3,
"chapters_per_volume": 10,
"include_sections": true,
"complexity": "medium"
}响应: 流式返回生成的大纲结构
移动节点
请求:
json
PUT /api/outline/5/move
{
"new_parent_id": 2,
"new_position": 3
}🖥️ 用户交互
大纲编辑页面
布局结构:
┌─────────────────────────────────────────┐
│ 项目标题 [AI生成] [AI续写] [保存] │
├─────────────────────────────────────────┤
│ ┌─────────────────┐ ┌────────────────┐ │
│ │ 大纲树结构 │ │ 节点详情编辑 │ │
│ │ │ │ │ │
│ │ ▼ 第一卷 │ │ 标题: ______ │ │
│ │ ▶ 第一章 │ │ │ │
│ │ ▶ 第二章 │ │ 描述: │ │
│ │ ▶ 第二卷 │ │ ____________ │ │
│ │ │ │ ____________ │ │
│ └─────────────────┘ └────────────────┘ │
└─────────────────────────────────────────┘交互操作
| 操作 | 方式 | 说明 |
|---|---|---|
| 展开/折叠 | 点击箭头 | 展开或折叠子节点 |
| 选中节点 | 单击节点 | 在右侧显示详情 |
| 添加子节点 | 右键菜单 / + 按钮 | 在当前节点下添加子节点 |
| 删除节点 | 右键菜单 / Delete | 删除节点及所有子节点 |
| 拖拽排序 | 拖拽节点 | 调整顺序或移动到其他父节点 |
| 编辑内容 | 双击 / 右侧面板 | 编辑标题和描述 |
AI 生成流程
1. 点击"AI生成大纲"
↓
2. 配置生成参数
- 卷数量
- 章节数量
- 复杂度
↓
3. 确认生成(会清空现有大纲)
↓
4. 流式展示生成过程
↓
5. 生成完成,可继续编辑AI 续写流程
1. 选中要续写的位置
↓
2. 点击"AI续写"
↓
3. 配置续写参数
- 续写章节数
- 情节方向提示
↓
4. 流式展示续写内容
↓
5. 确认采纳或修改⚙️ 技术实现
前端
路径: src/features/novel_outline/frontend/
| 文件 | 说明 |
|---|---|
api.ts | API 调用封装 |
pages/OutlinePage.tsx | 大纲编辑页面 |
components/OutlineTree.tsx | 大纲树组件 |
components/OutlineNode.tsx | 单个节点组件 |
components/NodeEditor.tsx | 节点编辑面板 |
后端
路径: src/features/novel_outline/backend/
| 文件 | 说明 |
|---|---|
models.py | OutlineNode 模型 |
schemas.py | Pydantic 验证模型 |
router.py | FastAPI 路由 |
services/outline_service.py | 大纲业务逻辑 |
services/ai_outline_service.py | AI 生成服务 |
树形数据处理
构建树结构:
python
def build_tree(nodes: List[OutlineNode]) -> List[dict]:
"""将扁平节点列表构建为树结构"""
node_map = {n.id: {...n, "children": []} for n in nodes}
tree = []
for node in nodes:
if node.parent_id is None:
tree.append(node_map[node.id])
else:
parent = node_map.get(node.parent_id)
if parent:
parent["children"].append(node_map[node.id])
return tree