# 分镜看板服务实现 **日期**:2026-02-04 **类型**:功能实现 **影响范围**:后端服务层、API 层 --- ## 变更概述 实现分镜看板服务(StoryboardBoardService),提供多轨道视图展示分镜及其关联资源的可视化看板功能。 --- ## 变更详情 ### 1. 创建 Schema 定义 **文件**:`server/app/schemas/storyboard_board.py` 创建了 4 个 Schema 类: - `StoryboardBoardItem` - 分镜看板项 - `StoryboardBoardTrack` - 分镜看板轨道 - `StoryboardBoardData` - 完整分镜看板数据 - `StoryboardReorder` - 分镜重新排序请求 ### 2. 创建 Service 层 **文件**:`server/app/services/storyboard_board_service.py` 实现了 `StoryboardBoardService` 类,提供以下功能: #### 核心方法 1. **get_storyboard_board_data()** - 获取项目的完整分镜看板数据 - 实时从分镜表计算,无独立数据存储 - 支持按轨道类型筛选 - 计算每个项的 start_time 和 end_time - 计算项目总时长 2. **reorder_storyboards()** - 调整分镜顺序 - 直接更新分镜表的 order_index 字段 - 使用事务批量更新 - 应用层验证分镜存在性 - 返回更新后的看板数据 3. **get_track_items()** - 获取指定轨道的所有项 - 支持 6 种轨道类型:storyboard/resource/video/sound/subtitle/voice - 验证轨道类型有效性 4. **get_items_by_time_range()** - 查询时间范围内的所有项 - 支持时间范围筛选 - 支持按轨道类型筛选 #### 私有方法 - `_calculate_storyboard_board_tracks()` - 实时计算分镜看板轨道数据 - `_get_track_name()` - 获取轨道显示名称 ### 3. 创建 API 路由 **文件**:`server/app/api/v1/storyboard_board.py` 实现了 4 个 API 端点: 1. **GET /projects/{project_id}/storyboard-board** - 获取项目分镜看板数据 - 查询参数:track_types(可选,逗号分隔) 2. **POST /projects/{project_id}/storyboard-board/reorder** - 调整分镜顺序 - 请求体:storyboard_orders(分镜 ID 到新排序的映射) 3. **GET /projects/{project_id}/storyboard-board/tracks/{track_type}** - 获取指定轨道的所有项 - 路径参数:track_type 4. **GET /projects/{project_id}/storyboard-board/items/time-range** - 查询时间范围内的所有项 - 查询参数:start_time、end_time、track_types(可选) ### 4. 注册路由 **文件**:`server/app/api/v1/__init__.py` - 导入 `storyboard_board` 模块 - 注册路由:`api_router.include_router(storyboard_board.router, tags=["分镜看板"])` --- ## 技术栈符合性 ✅ **完全符合 jointo-tech-stack 规范**: 1. **日志系统** - 使用标准库 `logging` - 使用 %-formatting 格式化 - 异常日志使用 `exc_info=True` 2. **异步编程** - 所有数据库操作使用 `async/await` - 使用 `AsyncSession` 3. **无物理外键** - 应用层验证引用完整性 - 使用 `batch_exists()` 批量检查分镜存在性 4. **事务管理** - 批量更新使用 `async with self.db.begin()` 5. **统一响应格式** - 所有 API 使用 `ApiResponse` 格式 6. **权限检查** - 使用 `ProjectRepository.check_user_permission()` - 区分 viewer 和 editor 权限 --- ## 设计特点 ### 1. 无独立数据存储 分镜看板服务不创建独立的数据表,所有数据实时从分镜表计算: - **优点**: - 避免数据冗余 - 自动同步(分镜变化立即反映到看板) - 简化维护(无需维护同步逻辑) - **实现**: - 按 order_index 排序分镜 - 累积计算每个分镜的时间位置 - 实时组装轨道数据 ### 2. 六种轨道类型 | 轨道类型 | 数据来源 | 说明 | |---------|---------|------| | storyboard | storyboards 表 | 分镜元素块 | | resource | storyboard_items 表 | 项目素材(角色、场景、道具) | | video | 待实现 | 分镜生成的视频 | | sound | 待实现 | 分镜搭配的音效 | | subtitle | 待实现 | 对白台词文本 | | voice | 待实现 | 对白生成的配音音频 | **注意**:当前实现仅包含 storyboard 轨道,其他轨道需要在后续实现中添加 Relationship 查询。 ### 3. 时间计算逻辑 ```python current_time = 0.0 for storyboard in storyboards: start_time = current_time duration = float(storyboard.actual_duration or storyboard.estimated_duration or 0) end_time = current_time + duration current_time = end_time ``` --- ## API 示例 ### 1. 获取分镜看板数据 **请求**: ```http GET /api/v1/projects/019c0361-ea8e-7d43-bbcc-edd8eea06bb6/storyboard-board?track_types=storyboard,video ``` **响应**: ```json { "success": true, "code": 200, "message": "分镜看板数据获取成功", "data": { "project_id": "019c0361-ea8e-7d43-bbcc-edd8eea06bb6", "total_duration": 120.5, "storyboard_count": 15, "tracks": [ { "type": "storyboard", "name": "分镜轨道", "items": [ { "id": "019c0361-ea8e-7d43-bbcc-edd8eea06bb7", "type": "storyboard", "start_time": 0, "end_time": 5.5, "data": { "id": "019c0361-ea8e-7d43-bbcc-edd8eea06bb7", "title": "开场镜头", "shot_size": 3, "camera_movement": 1 } } ] } ] }, "timestamp": "2026-02-04T10:00:00Z" } ``` ### 2. 调整分镜顺序 **请求**: ```http POST /api/v1/projects/019c0361-ea8e-7d43-bbcc-edd8eea06bb6/storyboard-board/reorder Content-Type: application/json { "storyboard_orders": { "019c0361-ea8e-7d43-bbcc-edd8eea06bb7": 0, "019c0361-ea8e-7d43-bbcc-edd8eea06bb8": 1, "019c0361-ea8e-7d43-bbcc-edd8eea06bb9": 2 } } ``` **响应**: ```json { "success": true, "code": 200, "message": "分镜顺序调整成功", "data": { "project_id": "019c0361-ea8e-7d43-bbcc-edd8eea06bb6", "total_duration": 120.5, "storyboard_count": 15, "tracks": [...] }, "timestamp": "2026-02-04T10:00:00Z" } ``` --- ## 后续优化建议 ### 1. 性能优化 - **缓存策略**:使用 Redis 缓存分镜看板数据(5 分钟) - **分页加载**:对于大型项目(100+ 分镜)实现分页 - **前端虚拟滚动**:只渲染可见区域的轨道项 ### 2. 功能扩展 - **实现其他轨道**:resource、video、sound、subtitle、voice - **添加 Relationship**:在 Storyboard 模型中配置关联关系 - **预加载关联数据**:使用 `selectinload()` 减少 N+1 查询 ### 3. 数据完整性 - **引用计数维护**:在 StoryboardService 中维护 project_resources.usage_count - **删除保护**:usage_count > 0 的素材禁止删除 --- ## 相关文档 - [分镜管理服务](./2026-02-04-storyboard-service-implementation.md) - [需求文档:分镜看板服务](../../requirements/backend/04-services/project/storyboard-board-service.md) - [ADR-008: 分镜统一关联表](../../architecture/adrs/008-storyboard-unified-association-table.md) --- ## 测试建议 ### 单元测试 - `test_get_storyboard_board_data()` - 测试获取看板数据 - `test_reorder_storyboards()` - 测试调整分镜顺序 - `test_get_track_items()` - 测试获取轨道数据 - `test_get_items_by_time_range()` - 测试时间范围查询 - `test_calculate_storyboard_board_tracks()` - 测试轨道计算逻辑 ### 集成测试 - `test_storyboard_board_api_get()` - 测试 GET 端点 - `test_storyboard_board_api_reorder()` - 测试 POST 端点 - `test_storyboard_board_api_tracks()` - 测试轨道端点 - `test_storyboard_board_api_time_range()` - 测试时间范围端点 --- **变更作者**:Kiro AI **审核状态**:待审核 **部署状态**:待部署