You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10 KiB
10 KiB
剧本表结构创建
变更日期:2026-02-03
变更类型:数据库迁移
影响范围:后端数据库、剧本管理功能
变更概述
创建剧本管理相关的数据库表结构,支持剧本文件上传、解析和元素标签管理。
变更内容
1. 新增表结构
1.1 screenplays 表
剧本主表,存储剧本基本信息和内容。
核心字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
screenplay_id |
UUID | 主键(UUID v7) |
project_id |
UUID | 所属项目 ID |
name |
TEXT | 剧本名称 |
type |
SMALLINT | 剧本类型:1=file, 2=manual |
file_url |
TEXT | 文件 URL(type=1 时) |
file_name |
TEXT | 原始文件名 |
file_size |
BIGINT | 文件大小(字节) |
mime_type |
TEXT | 文件 MIME 类型 |
content |
TEXT | 剧本文本内容(Markdown) |
word_count |
INTEGER | 字数统计 |
parsing_status |
SMALLINT | 解析状态:0=idle, 1=pending, 2=parsing, 3=completed, 4=failed |
parsing_error |
TEXT | 解析错误信息 |
parsed_at |
TIMESTAMPTZ | 解析完成时间 |
display_order |
INTEGER | 显示顺序 |
created_at |
TIMESTAMPTZ | 创建时间 |
updated_at |
TIMESTAMPTZ | 更新时间 |
deleted_at |
TIMESTAMPTZ | 软删除时间 |
索引:
idx_screenplays_project_id- 项目查询idx_screenplays_type- 类型筛选idx_screenplays_parsing_status- 解析状态查询(部分索引)idx_screenplays_order- 排序查询idx_screenplays_deleted- 软删除过滤(部分索引)idx_screenplays_name_trgm- 名称全文搜索(GIN)idx_screenplays_content_trgm- 内容全文搜索(GIN)
约束:
screenplays_type_check- 类型范围:1-2screenplays_parsing_status_check- 解析状态范围:0-4screenplays_word_count_check- 字数非负screenplays_content_check- 内容完整性:file 类型必须有 file_url,manual 类型必须有 content
触发器:
update_screenplays_updated_at- 自动更新 updated_at 字段
1.2 screenplay_tags 表
剧本元素标签表,存储剧本中的角色、场景、道具等元素及其标签。
核心字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
tag_id |
UUID | 主键(UUID v7) |
screenplay_id |
UUID | 所属剧本 ID |
element_type |
SMALLINT | 元素类型:1=character, 2=scene, 3=prop, 4=sound, 5=other |
element_name |
TEXT | 元素名称(如"孙悟空"、"咖啡厅") |
tag_label |
TEXT | 标签名称(如"少年"、"白天") |
description |
TEXT | 元素描述 |
metadata |
JSONB | 扩展字段 |
display_order |
INTEGER | 显示顺序 |
created_at |
TIMESTAMPTZ | 创建时间 |
updated_at |
TIMESTAMPTZ | 更新时间 |
索引:
idx_screenplay_tags_screenplay_id- 剧本查询idx_screenplay_tags_element_type- 元素类型筛选idx_screenplay_tags_element_name- 元素名称查询idx_screenplay_tags_order- 排序查询idx_screenplay_tags_metadata_gin- JSONB 字段查询(GIN)idx_screenplay_tags_element_name_trgm- 元素名称全文搜索(GIN)idx_screenplay_tags_tag_label_trgm- 标签名称全文搜索(GIN)
约束:
screenplay_tags_element_type_check- 元素类型范围:1-5screenplay_tags_unique- 唯一约束:(screenplay_id, element_type, element_name, tag_label)
触发器:
update_screenplay_tags_updated_at- 自动更新 updated_at 字段
2. 设计原则
2.1 符合 jointo-tech-stack 规范
- ✅ UUID v7:所有主键使用 UUID v7(应用层生成)
- ✅ TIMESTAMPTZ:所有时间字段使用
TIMESTAMP(timezone=True) - ✅ SMALLINT 枚举:类型字段使用 SMALLINT 存储枚举值
- ✅ 应用层外键:无物理外键约束,应用层保证引用完整性
- ✅ 索引策略:合理使用 B-Tree、GIN、部分索引
- ✅ 触发器:自动更新 updated_at 字段
2.2 文件解析状态管理
支持完整的文件解析生命周期:
idle (0) → pending (1) → parsing (2) → completed (3)
↘ failed (4)
- idle:未解析(TXT/Markdown 直接读取后跳过此状态)
- pending:待解析(任务已提交,等待 Celery Worker 处理)
- parsing:解析中(Worker 正在处理)
- completed:解析完成(content 字段已填充)
- failed:解析失败(parsing_error 字段记录错误信息)
2.3 多种剧本类型支持
-
type=1 (file):上传文件类型
- 支持格式:TXT、Markdown、DOCX、PDF、RTF、DOC
- 必须字段:file_url, file_name, mime_type
- 解析后填充:content, word_count
-
type=2 (manual):手动输入类型
- 用户直接输入剧本文本
- 必须字段:content
- 自动计算:word_count
2.4 全文搜索支持
使用 PostgreSQL 的 pg_trgm 扩展实现全文搜索:
- screenplays.name - 剧本名称搜索
- screenplays.content - 剧本内容搜索
- screenplay_tags.element_name - 元素名称搜索
- screenplay_tags.tag_label - 标签名称搜索
3. 迁移文件
文件路径:server/alembic/versions/20260203_1200_create_screenplays_tables.py
Revision ID:20260203_1200
依赖:20260203_1100 (create_storyboard_resources_tables)
使用示例
1. 创建文件类型剧本
from uuid import uuid7
from datetime import datetime, UTC
screenplay = {
'screenplay_id': uuid7(),
'project_id': project_id,
'name': '第一集剧本',
'type': 1, # file
'file_url': 'https://storage.jointo.ai/screenplays/1/abc123.pdf',
'file_name': '第一集剧本.pdf',
'file_size': 1024000,
'mime_type': 'application/pdf',
'parsing_status': 1, # pending
'display_order': 0,
'created_at': datetime.now(UTC),
'updated_at': datetime.now(UTC)
}
2. 更新解析状态
# 解析完成
await screenplay_repo.update(screenplay_id, {
'content': '# 第一场 咖啡厅 下午\n\n张三坐在咖啡厅里...',
'word_count': 5000,
'parsing_status': 3, # completed
'parsed_at': datetime.now(UTC)
})
# 解析失败
await screenplay_repo.update(screenplay_id, {
'parsing_status': 4, # failed
'parsing_error': 'PDF 文件损坏,无法解析'
})
3. 创建剧本元素标签
# 角色标签
character_tag = {
'tag_id': uuid7(),
'screenplay_id': screenplay_id,
'element_type': 1, # character
'element_name': '孙悟空',
'tag_label': '少年',
'description': '主角,活泼好动',
'metadata': {
'appearance': '金色头发,红色衣服',
'personality': '勇敢、正义',
'age': '16'
},
'display_order': 0,
'created_at': datetime.now(UTC),
'updated_at': datetime.now(UTC)
}
# 场景标签
scene_tag = {
'tag_id': uuid7(),
'screenplay_id': screenplay_id,
'element_type': 2, # scene
'element_name': '咖啡厅',
'tag_label': '白天',
'description': '温馨的咖啡厅',
'metadata': {
'location_type': 'indoor',
'lighting': 'natural',
'atmosphere': 'cozy'
},
'display_order': 1,
'created_at': datetime.now(UTC),
'updated_at': datetime.now(UTC)
}
4. 全文搜索
-- 搜索剧本名称
SELECT * FROM screenplays
WHERE name % '第一集'
ORDER BY similarity(name, '第一集') DESC;
-- 搜索剧本内容
SELECT * FROM screenplays
WHERE content % '咖啡厅'
ORDER BY similarity(content, '咖啡厅') DESC;
-- 搜索元素名称
SELECT * FROM screenplay_tags
WHERE element_name % '孙悟空'
ORDER BY similarity(element_name, '孙悟空') DESC;
后续工作
1. 创建 Model 类
server/app/models/screenplay.py- Screenplay 模型server/app/models/screenplay_tag.py- ScreenplayTag 模型
2. 创建 Repository 类
server/app/repositories/screenplay_repository.pyserver/app/repositories/screenplay_tag_repository.py
3. 创建 Schema 类
server/app/schemas/screenplay.py- 请求/响应 Schema
4. 完善 Service 类
- 补充
ScreenplayFileParserService的数据库操作 - 创建
ScreenplayService- 剧本管理服务 - 创建
ScreenplayTagService- 标签管理服务
5. 创建 API 路由
server/app/api/v1/screenplays.py- 剧本 CRUD APIserver/app/api/v1/screenplay_tags.py- 标签 CRUD API
6. 创建 Celery 任务
server/app/tasks/screenplay_tasks.py- 文件解析异步任务
测试建议
1. 数据库迁移测试
# 升级
docker exec jointo-server-app alembic upgrade head
# 验证表结构
docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d screenplays"
docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d screenplay_tags"
# 降级测试
docker exec jointo-server-app alembic downgrade -1
docker exec jointo-server-app alembic upgrade head
2. 约束测试
-- 测试类型约束
INSERT INTO screenplays (screenplay_id, project_id, name, type)
VALUES (gen_random_uuid(), gen_random_uuid(), 'Test', 3); -- 应失败
-- 测试内容完整性约束
INSERT INTO screenplays (screenplay_id, project_id, name, type)
VALUES (gen_random_uuid(), gen_random_uuid(), 'Test', 1); -- 应失败(缺少 file_url)
-- 测试唯一约束
INSERT INTO screenplay_tags (tag_id, screenplay_id, element_type, element_name, tag_label)
VALUES
(gen_random_uuid(), 'xxx', 1, '孙悟空', '少年'),
(gen_random_uuid(), 'xxx', 1, '孙悟空', '少年'); -- 应失败(重复)
3. 索引性能测试
-- 测试全文搜索性能
EXPLAIN ANALYZE
SELECT * FROM screenplays
WHERE name % '第一集';
-- 测试 JSONB 查询性能
EXPLAIN ANALYZE
SELECT * FROM screenplay_tags
WHERE metadata @> '{"age": "16"}';
相关文档
变更日期:2026-02-03
迁移文件:20260203_1200_create_screenplays_tables.py
状态:✅ 已完成