Skip to content

章节系统

管理小说的具体章节内容,支持独立编辑和状态追踪


📖 系统概述

章节系统负责管理小说的具体内容,采用扁平结构存储章节正文。每个章节可以独立编辑,也可以与大纲节点关联,实现结构化创作。系统支持多种状态追踪,帮助作者清晰掌握创作进度。


🎯 功能特性

1. 章节管理

功能说明
创建章节新建空白章节或从大纲创建
编辑内容富文本编辑,支持实时保存
排序调整通过 chapter_number 控制顺序
批量操作批量删除、批量状态更新

2. 大纲关联

章节可以与大纲节点(OutlineNode)建立一对一关联:

OutlineNode (chapter 类型)

    └──→ Chapter
         - outline_node_id = OutlineNode.id
         - 自动同步标题
         - 内容独立存储

关联优势:

  • 结构清晰:大纲和内容对应
  • 导航便捷:从大纲直接跳转到章节
  • 进度追踪:大纲视图显示章节完成状态

3. 状态追踪

状态说明
草稿draft正在编写,未完成
已完成completed作者已完成编写
AI 生成ai_generated由 AI 生成的内容
draft (草稿)

    ├──→ completed (已完成)  ← 手动完成

    └──→ ai_generated (AI生成) ← AI 生成

            └──→ completed  ← 审核后标记完成

4. 字数统计

  • 单章字数: 实时计算章节内容字数
  • 累计统计: 汇总到项目总字数
  • 目标追踪: 支持设置章节目标字数

🗄️ 数据模型

Chapter 模型

python
class Chapter(models.Model):
    id = fields.IntField(pk=True)
    uuid = fields.UUIDField()                    # 外部引用UUID
    project_id = fields.IntField()               # 所属项目
    outline_node_id = fields.IntField(null=True) # 关联大纲节点(可选)
    title = fields.CharField(max_length=200)     # 章节标题
    content = fields.TextField(null=True)        # 章节正文
    chapter_number = fields.IntField(default=0)  # 章节序号
    word_count = fields.IntField(default=0)      # 字数
    status = fields.CharField(max_length=20, default="draft")  # 状态
    created_at = fields.DatetimeField(auto_now_add=True)
    updated_at = fields.DatetimeField(auto_now=True)

    class Meta:
        table = "chapters"

关联关系

NovelProject

    ├──→ Chapter (多个)
    │       │
    │       └──→ OutlineNode (可选,一对一)

    └──→ chapter_ids (JSON): 存储章节顺序

🔌 API 接口

基础路径

/api/chapter

接口列表

方法路径说明
GET/project/{project_id}获取项目所有章节
POST/创建章节
GET/{chapter_id}获取章节详情
PUT/{chapter_id}更新章节
DELETE/{chapter_id}删除章节
PUT/{chapter_id}/status更新章节状态
PUT/{chapter_id}/content更新章节内容(自动保存)
POST/from-outline/{node_id}从大纲节点创建章节
PUT/reorder调整章节顺序

请求/响应示例

创建章节

请求:

json
POST /api/chapter
{
  "project_id": 1,
  "title": "第一章 初遇",
  "chapter_number": 1,
  "outline_node_id": 5
}

响应:

json
{
  "id": 1,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "project_id": 1,
  "title": "第一章 初遇",
  "content": null,
  "chapter_number": 1,
  "word_count": 0,
  "status": "draft",
  "outline_node_id": 5,
  "created_at": "2026-01-04T12:00:00Z"
}

更新章节内容

请求:

json
PUT /api/chapter/1/content
{
  "content": "清晨的阳光透过窗帘的缝隙..."
}

响应:

json
{
  "id": 1,
  "word_count": 15,
  "updated_at": "2026-01-04T12:30:00Z"
}

获取项目章节列表

请求:

GET /api/chapter/project/1?status=draft

响应:

json
{
  "chapters": [
    {
      "id": 1,
      "title": "第一章 初遇",
      "chapter_number": 1,
      "word_count": 2500,
      "status": "completed"
    },
    {
      "id": 2,
      "title": "第二章 误会",
      "chapter_number": 2,
      "word_count": 1800,
      "status": "draft"
    }
  ],
  "total_word_count": 4300
}

🖥️ 用户交互

章节列表页面

布局结构:

┌─────────────────────────────────────────────────────┐
│  项目名称 > 章节管理        [新建章节] [批量操作]    │
├─────────────────────────────────────────────────────┤
│  筛选: [全部▼] [草稿] [已完成] [AI生成]             │
├─────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────┐   │
│  │ □ 第一章 初遇          2,500字  ● 已完成    │   │
│  │   最后编辑: 2026-01-04 12:30                │   │
│  ├─────────────────────────────────────────────┤   │
│  │ □ 第二章 误会          1,800字  ○ 草稿      │   │
│  │   最后编辑: 2026-01-04 14:20                │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘

章节编辑页面

布局结构:

┌─────────────────────────────────────────────────────┐
│  ← 返回  第一章 初遇  [状态: 草稿▼]  [保存] [AI续写]│
├─────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────┐   │
│  │                                             │   │
│  │          章节内容编辑区                      │   │
│  │                                             │   │
│  │  清晨的阳光透过窗帘的缝隙...                 │   │
│  │                                             │   │
│  │                                             │   │
│  └─────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────┤
│  字数: 2,500 / 目标: 3,000    自动保存: 12:30:45   │
└─────────────────────────────────────────────────────┘

交互操作

操作方式说明
新建章节点击按钮弹出创建对话框
编辑章节点击章节进入编辑页面
切换状态下拉菜单快速切换章节状态
删除章节右键/按钮确认后删除
拖拽排序拖拽行调整章节顺序
自动保存自动编辑时自动保存(防抖)

从大纲创建章节

1. 在大纲系统中选中 chapter 类型节点

2. 点击"创建章节"

3. 自动创建关联章节
   - 标题同步大纲节点标题
   - 建立 outline_node_id 关联

4. 跳转到章节编辑页面

⚙️ 技术实现

前端

路径: src/features/chapter/frontend/

文件说明
api.tsAPI 调用封装
pages/ChapterListPage.tsx章节列表页
pages/ChapterEditPage.tsx章节编辑页
components/ChapterEditor.tsx编辑器组件
components/ChapterCard.tsx章节卡片组件
hooks/useAutoSave.ts自动保存 Hook

后端

路径: src/features/chapter/backend/

文件说明
models.pyChapter 模型
schemas.pyPydantic 验证模型
router.pyFastAPI 路由
services/chapter_service.py章节业务逻辑
services/word_count_service.py字数统计服务

自动保存实现

typescript
// useAutoSave.ts
const useAutoSave = (content: string, onSave: Function) => {
  const debouncedSave = useDebouncedCallback(
    async (content) => {
      await onSave(content)
    },
    2000 // 2秒防抖
  )

  useEffect(() => {
    if (content) {
      debouncedSave(content)
    }
  }, [content])
}

字数统计

python
def count_words(content: str) -> int:
    """统计中文字数(含标点)"""
    if not content:
        return 0
    # 移除空白字符后计算长度
    return len(content.replace(" ", "").replace("\n", ""))

🔗 关联文档