Skip to content

大纲系统

以树状结构组织小说的整体框架和情节脉络


📖 系统概述

大纲系统采用树状结构管理小说的整体框架,支持卷、章、小节三层层级。通过 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/generateAI 生成大纲
POST/continueAI 续写大纲

请求/响应示例

获取大纲树

请求:

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.tsAPI 调用封装
pages/OutlinePage.tsx大纲编辑页面
components/OutlineTree.tsx大纲树组件
components/OutlineNode.tsx单个节点组件
components/NodeEditor.tsx节点编辑面板

后端

路径: src/features/novel_outline/backend/

文件说明
models.pyOutlineNode 模型
schemas.pyPydantic 验证模型
router.pyFastAPI 路由
services/outline_service.py大纲业务逻辑
services/ai_outline_service.pyAI 生成服务

树形数据处理

构建树结构:

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

🔗 关联文档