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.
12 KiB
12 KiB
分镜服务完整实现总结
日期:2026-02-04
类型:功能实现总结
影响范围:后端服务层、API 层、数据库
实现概述
完成了分镜管理系统的核心功能实现,包括:
- 分镜基础管理(CRUD)
- 分镜看板服务(可视化展示)
- 项目素材引用计数(已有字段支持)
已完成功能
1. 分镜基础管理
数据库表:
- ✅
storyboards- 分镜主表 - ✅
storyboard_items- 分镜元素关联表(统一管理 ElementTag 和 Resource)
核心功能:
- ✅ 分镜 CRUD 操作
- ✅ 分镜排序管理(
order_index字段) - ✅ 分镜元素关联(ElementTag + Resource 统一管理)
- ✅ 分镜筛选(按景别、运镜)
- ✅ 分镜搜索(全文搜索)
- ✅ 时长统计
API 端点(13个):
- ✅ GET
/storyboards- 获取分镜列表 - ✅ GET
/storyboards/{id}- 获取分镜详情 - ✅ POST
/storyboards- 创建分镜 - ✅ PATCH
/storyboards/{id}- 更新分镜 - ✅ DELETE
/storyboards/{id}- 删除分镜 - ✅ POST
/storyboards/reorder- 重新排序 - ✅ GET
/storyboards/filter- 筛选分镜 - ✅ GET
/storyboards/search- 搜索分镜 - ✅ GET
/storyboards/statistics/duration- 时长统计 - ✅ POST
/storyboards/{id}/items- 添加元素 - ✅ DELETE
/storyboards/{id}/items/{item_id}- 移除元素 - ✅ PATCH
/storyboards/{id}/items/{item_id}- 更新元素 - ✅ POST
/storyboards/{id}/items/reorder- 调整元素顺序
2. 分镜看板服务
设计特点:
- ✅ 无独立数据存储(实时从分镜表计算)
- ✅ 六种轨道类型支持(当前实现 storyboard 轨道)
- ✅ 时间轴计算(累积计算每个分镜的 start_time 和 end_time)
API 端点(4个):
- ✅ GET
/projects/{project_id}/storyboard-board- 获取看板数据 - ✅ POST
/projects/{project_id}/storyboard-board/reorder- 调整分镜顺序 - ✅ GET
/projects/{project_id}/storyboard-board/tracks/{track_type}- 获取轨道数据 - ✅ GET
/projects/{project_id}/storyboard-board/items/time-range- 时间范围查询
3. 项目素材引用计数
数据库字段:
- ✅
project_resources.usage_count- 引用计数字段(已存在) - ✅ CHECK 约束:
usage_count >= 0 - ✅ 索引:
idx_project_resources_usage_count
实现状态:
- ⚠️ 字段已存在,但引用计数维护逻辑需要在
StoryboardService中实现 - ⚠️ 当前
add_element_to_storyboard()和remove_element_from_storyboard()方法未包含计数更新逻辑
技术栈符合性
✅ 完全符合 jointo-tech-stack 规范:
-
数据库设计
- UUID v7 应用层生成(
generate_uuid()) - 无物理外键约束
- 枚举类型使用 SMALLINT + IntEnum
- 时间戳使用 TIMESTAMPTZ
- 全文搜索索引(GIN + pg_trgm)
- UUID v7 应用层生成(
-
日志系统
- 使用标准库
logging - 使用 %-formatting 格式化
- 异常日志使用
exc_info=True
- 使用标准库
-
异步编程
- 所有数据库操作使用
async/await - 使用
AsyncSession
- 所有数据库操作使用
-
事务管理
- Repository 使用
flush() - Service 使用
commit() - 批量操作使用事务
- Repository 使用
-
统一响应格式
- 所有 API 使用
ApiResponse格式
- 所有 API 使用
数据库表结构
storyboards 表
| 字段 | 类型 | 说明 |
|---|---|---|
| storyboard_id | UUID | 主键(UUID v7) |
| project_id | UUID | 所属项目 |
| title | VARCHAR(255) | 分镜标题 |
| description | TEXT | 分镜描述 |
| shooting_description | TEXT | 拍摄描述 |
| shot_size | SMALLINT | 景别类型(1-8) |
| camera_movement | SMALLINT | 运镜类型(1-9) |
| estimated_duration | NUMERIC(10,3) | 预估时长(秒) |
| actual_duration | NUMERIC(10,3) | 实际时长(秒) |
| start_time | NUMERIC(10,3) | 时间轴开始时间 |
| end_time | NUMERIC(10,3) | 时间轴结束时间 |
| thumbnail_url | TEXT | 缩略图 URL |
| thumbnail_id | UUID | 缩略图附件 ID |
| order_index | INTEGER | 显示顺序(镜号) |
| transition_type | TEXT | 转场类型 |
| transition_duration | NUMERIC(5,2) | 转场时长 |
| metadata | JSONB | 扩展字段 |
| created_at | TIMESTAMPTZ | 创建时间 |
| updated_at | TIMESTAMPTZ | 更新时间 |
索引:
idx_storyboards_project_ididx_storyboards_orderidx_storyboards_shot_sizeidx_storyboards_camera_movementidx_storyboards_thumbnail_ididx_storyboards_metadata_ginidx_storyboards_title_trgm(全文搜索)idx_storyboards_description_trgm(全文搜索)idx_storyboards_shooting_description_trgm(全文搜索)
约束:
storyboards_order_unique:(project_id, order_index)唯一storyboards_time_check:end_time > start_timestoryboards_duration_check:时长必须 > 0
storyboard_items 表
| 字段 | 类型 | 说明 |
|---|---|---|
| item_id | UUID | 主键(UUID v7) |
| storyboard_id | UUID | 所属分镜 |
| item_type | SMALLINT | 元素类型(1=ElementTag, 2=Resource) |
| element_tag_id | UUID | 剧本元素标签 ID |
| resource_id | UUID | 项目素材 ID |
| element_name | TEXT | 元素名称(冗余) |
| tag_label | TEXT | 标签名称(冗余) |
| cover_url | TEXT | 封面 URL(冗余) |
| is_visible | BOOLEAN | 是否在画面内 |
| spatial_position | TEXT | 画面位置 |
| action_description | TEXT | 动作描述 |
| display_order | INTEGER | 显示顺序 |
| z_index | INTEGER | 视觉层级 |
| metadata | JSONB | 扩展属性 |
| created_at | TIMESTAMPTZ | 创建时间 |
索引:
idx_storyboard_items_storyboard_ididx_storyboard_items_element_tagidx_storyboard_items_resourceidx_storyboard_items_orderidx_storyboard_items_type
约束:
storyboard_items_one_fk_check:确保只有一个外键字段非空storyboard_items_tag_unique:(storyboard_id, element_tag_id)唯一storyboard_items_resource_unique:(storyboard_id, resource_id)唯一
待实现功能
1. 引用计数维护逻辑
需要在 StoryboardService 中实现:
async def add_element_to_storyboard(self, ...):
# ... 现有逻辑 ...
# 如果是项目素材,增加引用计数
if item_type == ItemType.RESOURCE and resource_id:
await self.session.execute(
update(ProjectResource)
.where(ProjectResource.project_resource_id == resource_id)
.values(usage_count=ProjectResource.usage_count + 1)
)
# ... 提交事务 ...
async def remove_element_from_storyboard(self, ...):
# ... 现有逻辑 ...
# 如果是项目素材,减少引用计数
if item.item_type == ItemType.RESOURCE and item.resource_id:
await self.session.execute(
update(ProjectResource)
.where(ProjectResource.project_resource_id == item.resource_id)
.values(usage_count=ProjectResource.usage_count - 1)
)
# ... 提交事务 ...
2. 删除保护
需要在 ProjectResourceService.delete_resource() 中添加:
async def delete_resource(self, user_id, resource_id, force=False):
resource = await self.repo.get_by_id(resource_id)
# 检查引用计数
if resource.usage_count > 0 and not force:
raise ValidationError(
"该素材正在被 %d 个分镜使用,无法删除。"
"请先从分镜中移除该素材,或使用强制删除。" % resource.usage_count
)
# ... 删除逻辑 ...
3. 其他轨道数据
在 StoryboardBoardService._calculate_storyboard_board_tracks() 中添加:
- resource 轨道(从
storyboard_items查询) - video 轨道(需要实现
storyboard_videos表) - sound 轨道(需要实现
storyboard_sound_effects表) - subtitle 轨道(需要实现
storyboard_dialogues表) - voice 轨道(需要实现
storyboard_voiceovers表)
性能优化建议
1. 分镜看板缓存
# 使用 Redis 缓存分镜看板数据(5 分钟)
cache_key = "storyboard-board:%s" % project_id
cached_data = await redis_client.get(cache_key)
if cached_data:
return StoryboardBoardData.parse_raw(cached_data)
# 计算数据并缓存
storyboard_board_data = await self.get_storyboard_board_data(...)
await redis_client.setex(cache_key, 300, storyboard_board_data.json())
2. 分页加载
对于大型项目(100+ 分镜),实现分页:
async def get_storyboard_board_data_paginated(
self, user_id, project_id, page=1, page_size=50
):
offset = (page - 1) * page_size
storyboards = await self.storyboard_repo.get_by_project(
project_id, order_by='order_index', limit=page_size, offset=offset
)
# ... 计算轨道数据 ...
3. 预加载关联数据
使用 selectinload() 减少 N+1 查询:
from sqlalchemy.orm import selectinload
storyboards = await session.execute(
select(Storyboard)
.where(Storyboard.project_id == project_id)
.options(
selectinload(Storyboard.items),
selectinload(Storyboard.generated_video),
selectinload(Storyboard.dialogues)
)
.order_by(Storyboard.order_index)
)
测试建议
单元测试
StoryboardService:
test_create_storyboard()- 测试创建分镜test_update_storyboard()- 测试更新分镜test_delete_storyboard()- 测试删除分镜test_reorder_storyboards()- 测试重新排序test_add_element_to_storyboard()- 测试添加元素test_remove_element_from_storyboard()- 测试移除元素test_search_storyboards()- 测试搜索功能
StoryboardBoardService:
test_get_storyboard_board_data()- 测试获取看板数据test_reorder_storyboards()- 测试调整分镜顺序test_get_track_items()- 测试获取轨道数据test_get_items_by_time_range()- 测试时间范围查询
集成测试
API 测试:
test_storyboard_crud_api()- 测试分镜 CRUD 端点test_storyboard_board_api()- 测试分镜看板端点test_storyboard_filter_api()- 测试筛选端点test_storyboard_search_api()- 测试搜索端点
相关文档
总结
分镜管理系统的核心功能已完整实现,包括:
- ✅ 分镜 CRUD 和排序管理
- ✅ 分镜元素关联(统一管理 ElementTag 和 Resource)
- ✅ 分镜筛选和搜索
- ✅ 分镜看板服务(实时计算,无独立存储)
- ✅ 数据库表结构和索引
- ✅ 17 个 API 端点
待完善:
- ⏸️ 引用计数维护逻辑(字段已存在,需添加更新逻辑)
- ⏸️ 其他轨道数据(resource、video、sound、subtitle、voice)
- ⏸️ 性能优化(缓存、分页、预加载)
- ⏸️ 单元测试和集成测试
系统已具备基础的分镜管理和可视化看板功能,可以支持分镜的创建、编辑、排序、筛选、搜索和看板展示。
变更作者:Kiro AI
审核状态:待审核
部署状态:待部署