# Storyboard API 响应格式统一与查询参数规范化 **日期**: 2026-02-11 **类型**: 重构 **影响范围**: `/api/v1/storyboards` 所有接口 ## 变更概述 1. 统一分镜 API 的响应格式,使其与项目级资源 API(`project_elements`)保持一致 2. 规范化查询参数命名,支持 camelCase 格式(前端友好) ## 问题描述 ### 1. 不一致的响应格式 1. **响应类型不一致** - `project_elements.py` 使用:`SuccessResponse[T]`(来自 `app.schemas.common`) - `storyboards.py` 使用:`ApiResponse[T]`(来自 `app.schemas.response`) 2. **返回方式不一致** - `project_elements.py`:直接返回 `SuccessResponse` 对象 - `storyboards.py`:使用 `success_response()` 函数返回字典 3. **序列化方式不一致** - `project_elements.py`:依赖 Pydantic 自动序列化 - `storyboards.py`:手动调用 `model_dump(by_alias=True, mode='json')` ### 2. 查询参数命名不规范 查询参数使用 snake_case,前端需要额外转换: - `project_id` 应支持 `projectId` - `page_size` 应支持 `pageSize` - `include_items` 应支持 `includeItems` - `shot_size` 应支持 `shotSize` - `camera_movement` 应支持 `cameraMovement` ## 解决方案 ### 1. 统一响应格式 采用 `project_elements.py` 的模式: - 使用 `SuccessResponse[T]` 作为响应模型 - 直接返回 `SuccessResponse` 对象 - 依赖 Pydantic 自动序列化 ### 2. 规范化查询参数 为所有 snake_case 查询参数添加 camelCase alias: ```python # 修改前 project_id: str = Query(..., description="项目ID") # 修改后 project_id: str = Query(..., alias="projectId", description="项目ID") ``` ## 修改内容 ### 1. 导入语句调整 ```python # 修改前 from app.schemas.response import ApiResponse, success_response # 修改后 from app.schemas.common import SuccessResponse ``` ### 2. 响应模型调整 所有接口的 `response_model` 从 `ApiResponse[T]` 改为 `SuccessResponse[T]` ### 3. 返回语句调整 ```python # 修改前 return success_response( data=response_data.model_dump(by_alias=True, mode='json'), message="分镜列表获取成功" ) # 修改后 return SuccessResponse( data=response_data, message="分镜列表获取成功" ) ``` ### 4. 查询参数 alias 添加 | 参数名 | alias | 接口 | |--------|-------|------| | `project_id` | `projectId` | 所有列表/筛选/搜索接口 | | `page_size` | `pageSize` | 所有分页接口 | | `include_items` | `includeItems` | 列表和详情接口 | | `shot_size` | `shotSize` | 筛选接口 | | `camera_movement` | `cameraMovement` | 筛选接口 | ## 涉及的接口(共 14 个) ### 分镜 CRUD 1. `GET /storyboards` - 获取分镜列表 ✅ 响应格式 + 查询参数 2. `POST /storyboards` - 创建分镜 ✅ 响应格式 3. `GET /storyboards/{storyboard_id}` - 获取分镜详情 ✅ 响应格式 + 查询参数 4. `PUT /storyboards/{storyboard_id}` - 更新分镜 ✅ 响应格式 5. `DELETE /storyboards/{storyboard_id}` - 删除分镜 ✅ 响应格式 ### 排序管理 6. `POST /storyboards/reorder` - 重新排序分镜 ✅ 响应格式 ### 筛选和搜索 7. `GET /storyboards/filter` - 按景别和运镜筛选 ✅ 响应格式 + 查询参数 8. `GET /storyboards/search` - 全文搜索分镜 ✅ 响应格式 + 查询参数 9. `GET /storyboards/statistics/duration` - 获取时长统计 ✅ 响应格式 + 查询参数 ### 元素关联管理 10. `POST /storyboards/{storyboard_id}/items` - 添加元素到分镜 ✅ 响应格式 11. `GET /storyboards/{storyboard_id}/items` - 获取分镜的所有关联元素 ✅ 响应格式 12. `POST /storyboards/{storyboard_id}/items/reorder` - 批量调整元素顺序 ✅ 响应格式 13. `PATCH /storyboards/items/{item_id}` - 更新元素的关联属性 ✅ 响应格式 14. `DELETE /storyboards/items/{item_id}` - 从分镜移除元素 ✅ 响应格式 ## 技术细节 ### 响应格式保持不变 虽然内部实现改变,但最终的 JSON 响应格式完全一致: ```json { "success": true, "code": 200, "message": "分镜列表获取成功", "data": { "items": [...], "total": 100, "page": 1, "pageSize": 50, "totalPages": 2 }, "timestamp": "2026-02-11T10:30:00Z" } ``` ### 查询参数兼容性 添加 alias 后,两种格式都支持: ```bash # snake_case(兼容旧版) GET /api/v1/storyboards?project_id=xxx&page_size=50&include_items=true # camelCase(推荐,前端友好) GET /api/v1/storyboards?projectId=xxx&pageSize=50&includeItems=true ``` ### Pydantic 自动序列化优势 1. **自动处理 camelCase 转换** - Schema 中配置的 `alias_generator=to_camel` 自动生效 - 无需手动调用 `model_dump(by_alias=True)` 2. **类型安全** - 编译时类型检查 - IDE 自动补全支持 3. **代码简洁** - 减少重复的序列化代码 - 统一的返回模式 ## 影响评估 ### 前端影响 - **无影响**:响应格式完全一致 - **改进**:查询参数支持 camelCase,更符合前端命名习惯 ### 后端影响 - **代码简化**:移除了大量重复的 `model_dump()` 调用 - **一致性提升**:与其他 API 模块保持统一风格 - **维护性提升**:统一的模式更易于维护和扩展 ## 测试建议 1. **回归测试** - 测试所有 14 个接口的响应格式 - 验证 camelCase 字段名正确转换 - 验证嵌套对象(如 `items`、`metadata`)的序列化 2. **查询参数测试** - 测试 snake_case 参数(向后兼容) - 测试 camelCase 参数(新格式) - 测试混合使用两种格式 3. **集成测试** - 前端调用接口验证兼容性 - 验证分页、筛选、搜索功能正常 ## 相关文件 - `server/app/api/v1/storyboards.py` - 主要修改文件 - `server/app/schemas/common.py` - SuccessResponse 定义 - `server/app/schemas/storyboard.py` - 分镜 Schema 定义 ## 参考 - 参考实现:`server/app/api/v1/project_elements.py` - 查询参数规范:`server/app/api/v1/projects.py`、`server/app/api/v1/folders.py` - API 规范:项目统一使用 `SuccessResponse` 作为成功响应格式