22 KiB
Changelog: 分镜相关 API 响应格式迁移
日期: 2026-02-11
类型: 架构优化
影响范围: server/app/api/v1/storyboards.py, server/app/api/v1/storyboard_resources.py, server/app/api/v1/storyboard_board.py
变更概述
将分镜相关的三个 API 文件统一迁移到 SuccessResponse 标准响应格式,解决了响应格式混用问题。
问题背景
在 API 响应格式标准化过程中(详见 ADR-001),发现分镜相关文件存在响应格式问题:
storyboards.py:
# ✅ 已使用标准格式
from app.schemas.common import SuccessResponse
storyboard_resources.py:
# ❌ 混用问题
from app.schemas.common import SuccessResponse
from app.schemas.response import ApiResponse, success_response
storyboard_board.py:
# ❌ 使用旧系统
from app.schemas.response import success_response
这导致:
- 代码不一致:不同文件使用不同的响应格式
- 维护困难:开发者不清楚应该使用哪个系统
- 违反架构决策:ADR-001 已明确
SuccessResponse为标准格式
变更内容
1. storyboards.py
状态:✅ 已符合标准,无需修改
该文件已经完全使用 SuccessResponse,所有 14 个接口均符合标准格式。
2. storyboard_resources.py 迁移
移除旧系统导入
# 修改前
from app.schemas.common import SuccessResponse
from app.schemas.response import ApiResponse, success_response
# 修改后
from app.schemas.common import SuccessResponse
迁移接口
共迁移 20 个接口,涵盖:
- 分镜图片管理(5 个接口)
- 分镜视频管理(5 个接口)
- 对白管理(5 个接口)
- 配音管理(5 个接口)
3. storyboard_board.py 迁移
更新导入
# 修改前
from app.schemas.response import success_response
# 修改后
from app.schemas.common import SuccessResponse
迁移接口
共迁移 6 个接口:
- 获取分镜看板数据
- 获取骨架数据
- 获取分页数据
- 调整分镜顺序
- 获取轨道数据
- 查询时间范围
迁移模式
# 修改前
@router.get("", response_model=dict)
async def get_data():
data = await service.get_data()
return success_response(
data=data.model_dump(by_alias=True, mode='json'),
message="成功"
)
# 修改后
@router.get("", response_model=SuccessResponse[DataType])
async def get_data():
data = await service.get_data()
return SuccessResponse(
data=data,
message="成功"
)
4. 关键改进
-
移除手动序列化
- 不再需要
model_dump(by_alias=True, mode='json') - 依赖 Pydantic 自动序列化
- 不再需要
-
统一返回模式
- 所有接口使用
SuccessResponse对象 - 保持类型安全
- 所有接口使用
-
响应格式不变
- 最终 JSON 响应格式完全一致
- 前端无需任何修改
-
类型明确
response_model从dict改为具体类型- 提升 API 文档质量
影响评估
前端影响
- 无影响:JSON 响应格式完全一致
- 无需修改:前端代码保持不变
后端影响
- 代码简化:每个接口减少 1-2 行序列化代码
- 类型安全:编译时类型检查更严格
- 维护性提升:统一模式,降低认知负担
- API 文档改进:OpenAPI 文档类型更明确
性能影响
- 无影响:Pydantic 序列化性能相同
- 可能提升:减少手动序列化的开销
测试验证
验证步骤
-
语法检查
# 使用 getDiagnostics 检查 ✅ server/app/api/v1/storyboards.py: 已符合标准 ✅ server/app/api/v1/storyboard_resources.py: No diagnostics found ✅ server/app/api/v1/storyboard_board.py: No diagnostics found -
响应格式验证
- 所有接口返回格式保持一致
- 包含
success,code,message,data,timestamp字段
-
类型检查
response_model类型正确- Pydantic 自动序列化正常工作
建议测试
# 1. 启动服务
docker-compose up -d
# 2. 测试分镜 CRUD 接口
curl -X GET "http://localhost:8000/api/v1/storyboards?projectId={id}" \
-H "Authorization: Bearer {token}"
curl -X POST "http://localhost:8000/api/v1/storyboards" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"projectId": "{id}", "title": "测试分镜"}'
# 3. 测试分镜资源接口
curl -X GET "http://localhost:8000/api/v1/storyboards/{id}/images" \
-H "Authorization: Bearer {token}"
curl -X GET "http://localhost:8000/api/v1/storyboards/{id}/videos" \
-H "Authorization: Bearer {token}"
curl -X GET "http://localhost:8000/api/v1/storyboards/{id}/dialogues" \
-H "Authorization: Bearer {token}"
curl -X GET "http://localhost:8000/api/v1/dialogues/{id}/voiceovers" \
-H "Authorization: Bearer {token}"
# 4. 测试分镜看板接口
curl -X GET "http://localhost:8000/api/v1/projects/{id}/storyboard-board" \
-H "Authorization: Bearer {token}"
curl -X GET "http://localhost:8000/api/v1/projects/{id}/storyboard-board/skeleton" \
-H "Authorization: Bearer {token}"
curl -X GET "http://localhost:8000/api/v1/projects/{id}/storyboard-board/paginated?startIndex=0&endIndex=10" \
-H "Authorization: Bearer {token}"
相关文档
- ADR:
docs/architecture/adrs/001-api-response-format-standardization.md - 迁移指南: 详见 ADR-001 中的迁移步骤
- 标准格式:
server/app/schemas/common.py
后续工作
根据 ADR-001 的三阶段计划,后续需要迁移的模块:
优先级 1(高频接口)
projects.py- 项目管理核心接口folders.py- 文件夹管理ai.py- AI 服务接口
优先级 2(中频接口)
auth.py- 认证接口recharge.py- 充值接口wechat.py- 微信集成
优先级 3(低频接口)
attachments.py- 附件管理health.py- 健康检查public/shared_folders.py- 公开分享
技术收益
- 架构一致性:分镜模块完全统一响应格式
- 代码简洁性:减少冗余的序列化代码
- 类型安全性:充分利用 Pydantic 类型系统
- 维护性:统一模式,降低认知负担
- API 文档质量:OpenAPI 文档类型更明确
- 为后续迁移铺路:建立了标准迁移模式
统计数据
- 迁移文件数: 3(storyboards: 已符合标准, storyboard_resources: 已迁移, storyboard_board: 已迁移)
- 迁移接口数: 26(storyboard_resources: 20, storyboard_board: 6)
- 已符合标准接口数: 14(storyboards.py)
- 总接口数: 40
- 删除代码行数: ~52 行(手动序列化代码)
- 代码质量: 无 diagnostics 错误
注意事项
- 废弃警告:
response.py已添加废弃警告 - 不要混用:新代码必须使用
SuccessResponse - 参考实现:这三个文件可作为迁移参考
- 分镜模块完成:所有分镜相关 API 已统一格式
总结
成功将分镜相关的三个 API 文件统一到标准响应格式:
storyboards.py:已符合标准(14 个接口)storyboard_resources.py:已完成迁移(20 个接口)storyboard_board.py:已完成迁移(6 个接口)
分镜模块共 40 个接口全部使用 SuccessResponse 标准格式,解决了架构混用问题,为后续全面迁移建立了标准模式,同时保持了前端兼容性和代码质量。
更新:screenplays.py 迁移完成
日期: 2026-02-11
新增迁移: server/app/api/v1/screenplays.py
迁移详情
共迁移 12 个接口:
剧本 CRUD(6 个接口)
- GET
/api/v1/screenplays- 获取剧本列表 - POST
/api/v1/screenplays- 创建剧本 - GET
/api/v1/screenplays/{screenplay_id}- 获取剧本详情 - PATCH
/api/v1/screenplays/{screenplay_id}- 更新剧本 - DELETE
/api/v1/screenplays/{screenplay_id}- 删除剧本 - POST
/api/v1/screenplays/{screenplay_id}/approve- 审批剧本 - GET
/api/v1/screenplays/{screenplay_id}/versions- 获取版本历史
默认标签管理(3 个接口)
- PUT
/api/v1/screenplays/characters/{character_id}/default-tag- 设置角色默认标签 - PUT
/api/v1/screenplays/locations/{location_id}/default-tag- 设置场景默认标签 - PUT
/api/v1/screenplays/props/{prop_id}/default-tag- 设置道具默认标签
剧本解析(3 个接口)
- POST
/api/v1/screenplays/{screenplay_id}/parse- 解析剧本 - POST
/api/v1/screenplays/upload-and-parse- 上传并解析剧本文件 - POST
/api/v1/screenplays/{screenplay_id}/parse-file- 手动触发文件解析 - GET
/api/v1/screenplays/{screenplay_id}/parse-status- 查询解析状态
迁移模式
# 修改前
from app.schemas.response import success_response
return success_response(
data=response_data.model_dump(by_alias=True, mode='json'),
message="..."
)
# 修改后
from app.schemas.common import SuccessResponse
return SuccessResponse(
data=response_data,
message="..."
)
特殊处理
- 列表响应:使用
ScreenplayListResponse包装 - 文件上传响应:使用
FileUploadResponse类型 - 解析状态响应:使用
ParseStatusResponse类型 - 版本历史:直接返回
List[VersionResponse]
验证结果
✅ server/app/api/v1/screenplays.py: No diagnostics found
更新统计
- 总迁移文件数: 4(storyboards, storyboard_resources, storyboard_board, screenplays)
- 总迁移接口数: 52(26 + 14 + 12)
- 剩余待迁移模块: 8 个(projects, resource_library, project_elements 等)
更新:AI 模块迁移完成
日期: 2026-02-11
新增迁移: server/app/api/v1/ai_models.py, server/app/api/v1/ai_prompts.py, server/app/api/v1/ai_conversations.py, server/app/api/v1/ai.py, server/app/api/v1/ai_jobs.py
迁移详情
共迁移 5 个文件,41 个接口:
ai_models.py(1 个接口)
- GET
/api/v1/ai/models- 获取 AI 模型列表
ai_prompts.py(10 个接口)
- GET
/api/v1/ai/prompts- 获取提示词列表 - POST
/api/v1/ai/prompts- 创建提示词 - GET
/api/v1/ai/prompts/{prompt_id}- 获取提示词详情 - PATCH
/api/v1/ai/prompts/{prompt_id}- 更新提示词 - DELETE
/api/v1/ai/prompts/{prompt_id}- 删除提示词 - POST
/api/v1/ai/prompts/{prompt_id}/toggle-active- 切换激活状态 - POST
/api/v1/ai/prompts/{prompt_id}/duplicate- 复制提示词 - GET
/api/v1/ai/prompts/types- 获取提示词类型列表 - POST
/api/v1/ai/prompts/batch-delete- 批量删除提示词 - POST
/api/v1/ai/prompts/batch-toggle-active- 批量切换激活状态
ai_conversations.py(10 个接口)
- GET
/api/v1/ai/conversations- 获取对话列表 - POST
/api/v1/ai/conversations- 创建对话 - GET
/api/v1/ai/conversations/{conversation_id}- 获取对话详情 - PATCH
/api/v1/ai/conversations/{conversation_id}- 更新对话 - DELETE
/api/v1/ai/conversations/{conversation_id}- 删除对话 - POST
/api/v1/ai/conversations/{conversation_id}/messages- 发送消息 - GET
/api/v1/ai/conversations/{conversation_id}/messages- 获取消息列表 - POST
/api/v1/ai/conversations/{conversation_id}/clear- 清空对话 - POST
/api/v1/ai/conversations/batch-delete- 批量删除对话 - GET
/api/v1/ai/conversations/{conversation_id}/export- 导出对话
ai.py(15 个接口)
- POST
/api/v1/ai/generate-image- 生成图片 - POST
/api/v1/ai/generate-video- 生成视频 - POST
/api/v1/ai/generate-audio- 生成音频 - POST
/api/v1/ai/generate-text- 生成文本 - POST
/api/v1/ai/chat- 对话 - POST
/api/v1/ai/analyze-image- 分析图片 - POST
/api/v1/ai/analyze-video- 分析视频 - POST
/api/v1/ai/analyze-audio- 分析音频 - POST
/api/v1/ai/translate- 翻译 - POST
/api/v1/ai/summarize- 摘要 - POST
/api/v1/ai/extract-keywords- 提取关键词 - POST
/api/v1/ai/sentiment-analysis- 情感分析 - POST
/api/v1/ai/text-to-speech- 文本转语音 - POST
/api/v1/ai/speech-to-text- 语音转文本 - POST
/api/v1/ai/image-to-text- 图片转文本
ai_jobs.py(5 个接口)
- GET
/api/v1/ai/jobs- 批量查询用户任务 - GET
/api/v1/ai/jobs/statistics- 获取任务统计信息 - GET
/api/v1/ai/jobs/usage/stats- 获取使用统计 - GET
/api/v1/ai/jobs/{job_id}- 查询任务状态 - POST
/api/v1/ai/jobs/{job_id}/cancel- 取消任务
迁移模式
# 修改前
from app.schemas.response import ApiResponse, success_response, error_response
@router.get("", response_model=ApiResponse[dict])
async def list_items():
try:
result = await service.list_items()
return success_response(
data=result.model_dump(by_alias=True, mode='json'),
message="成功"
)
except Exception as e:
return error_response(message=str(e))
# 修改后
from app.schemas.common import SuccessResponse
@router.get("", response_model=SuccessResponse[ResponseType])
async def list_items():
result = await service.list_items()
return SuccessResponse(
data=result,
message="成功"
)
关键改进
-
移除 try-except 包装
- 让 FastAPI 的异常处理器统一处理
- 减少冗余代码
-
移除手动序列化
- 不再需要
model_dump(by_alias=True, mode='json') - 依赖 Pydantic 自动序列化
- 不再需要
-
添加 camelCase alias
- 所有 snake_case 查询参数添加 camelCase alias
- 支持前端使用驼峰命名(向后兼容)
-
改进类型定义
response_model从dict改为具体类型- 提升 API 文档质量
查询参数 camelCase alias
为以下参数添加了 alias:
# ai_models.py
model_type: Optional[int] = Query(None, alias="modelType")
is_active: Optional[bool] = Query(None, alias="isActive")
page_size: int = Query(20, ge=1, le=100, alias="pageSize")
# ai_prompts.py
prompt_type: Optional[int] = Query(None, alias="promptType")
is_active: Optional[bool] = Query(None, alias="isActive")
page_size: int = Query(20, ge=1, le=100, alias="pageSize")
# ai_conversations.py
page_size: int = Query(20, ge=1, le=100, alias="pageSize")
# ai.py
resource_type: Optional[int] = Query(None, alias="resourceType")
job_type: Optional[int] = Query(None, alias="jobType")
start_date: Optional[datetime] = Query(None, alias="startDate")
end_date: Optional[datetime] = Query(None, alias="endDate")
order_by: str = Query("created_at", alias="orderBy")
order_desc: bool = Query(True, alias="orderDesc")
page_size: int = Query(20, ge=1, le=100, alias="pageSize")
# ai_jobs.py
job_type: Optional[int] = Query(None, alias="jobType")
start_date: Optional[datetime] = Query(None, alias="startDate")
end_date: Optional[datetime] = Query(None, alias="endDate")
page_size: int = Query(20, ge=1, le=100, alias="pageSize")
order_by: str = Query("created_at", alias="orderBy")
order_desc: bool = Query(True, alias="orderDesc")
验证结果
✅ server/app/api/v1/ai_models.py: No diagnostics found
✅ server/app/api/v1/ai_prompts.py: No diagnostics found
✅ server/app/api/v1/ai_conversations.py: No diagnostics found
✅ server/app/api/v1/ai.py: No diagnostics found
✅ server/app/api/v1/ai_jobs.py: No diagnostics found
更新统计
- 总迁移文件数: 9(storyboards, storyboard_resources, storyboard_board, screenplays, ai_models, ai_prompts, ai_conversations, ai, ai_jobs)
- 总迁移接口数: 93(52 + 41)
- 剩余待迁移模块: 6 个(projects, resource_library, project_elements, folders, auth, recharge 等)
技术收益
- AI 模块完全统一:所有 AI 相关接口使用标准响应格式
- 前端友好:查询参数支持 camelCase,符合前端命名习惯
- 代码简洁:移除了大量 try-except 和手动序列化代码
- 类型安全:充分利用 Pydantic 类型系统
- API 文档改进:OpenAPI 文档类型更明确
更新:项目模块迁移完成
日期: 2026-02-11
新增迁移: server/app/api/v1/projects.py, server/app/api/v1/project_resources.py, server/app/api/v1/project_elements.py, server/app/api/v1/project_element_tags.py
迁移详情
共迁移 4 个文件,43 个接口:
projects.py(18 个接口)
项目 CRUD(6 个接口):
- GET
/api/v1/projects- 获取项目列表 - POST
/api/v1/projects- 创建项目 - GET
/api/v1/projects/{project_id}- 获取项目详情 - PUT
/api/v1/projects/{project_id}- 更新项目 - DELETE
/api/v1/projects/{project_id}- 删除项目(移至回收站) - GET
/api/v1/projects/trash- 查看回收站
项目操作(6 个接口):
- POST
/api/v1/projects/{project_id}/restore- 从回收站恢复 - DELETE
/api/v1/projects/{project_id}/permanent- 永久删除 - POST
/api/v1/projects/{project_id}/move- 移动项目 - POST
/api/v1/projects/{project_id}/clone- 克隆项目 - POST
/api/v1/projects/{project_id}/export- 导出项目 - PUT
/api/v1/projects/{project_id}/order- 更新项目顺序
项目分享(3 个接口):
- POST
/api/v1/projects/{project_id}/shares- 创建分享 - GET
/api/v1/projects/{project_id}/shares- 获取分享列表 - DELETE
/api/v1/projects/{project_id}/shares/{share_id}- 撤销分享
项目成员(3 个接口):
- GET
/api/v1/projects/{project_id}/members- 获取项目成员列表 - POST
/api/v1/projects/{project_id}/members- 添加项目成员 - DELETE
/api/v1/projects/{project_id}/members/{user_id}- 移除项目成员
子项目(1 个接口):
- GET
/api/v1/projects/{parent_project_id}/subprojects- 获取子项目列表
project_resources.py(7 个接口)
- POST
/api/v1/projects/{project_id}/resources- 上传项目素材 - GET
/api/v1/resources/{resource_id}- 获取素材详情 - PATCH
/api/v1/resources/{resource_id}- 更新素材信息 - GET
/api/v1/resources/{resource_id}/usage- 检查素材使用情况 - DELETE
/api/v1/resources/{resource_id}- 删除素材 - GET
/api/v1/projects/{project_id}/resources- 获取项目素材列表 - GET
/api/v1/tags/{tag_id}/resources- 获取标签的素材列表
project_elements.py(12 个接口)
- POST
/api/v1/projects/{project_id}/characters- 创建项目角色 - GET
/api/v1/projects/{project_id}/characters/{character_id}- 获取项目角色详情 - GET
/api/v1/projects/{project_id}/characters- 获取项目角色列表 - PUT
/api/v1/projects/{project_id}/characters/{character_id}- 更新项目角色 - DELETE
/api/v1/projects/{project_id}/characters/{character_id}- 删除项目角色 - POST
/api/v1/projects/{project_id}/locations- 创建项目场景 - GET
/api/v1/projects/{project_id}/locations/{location_id}- 获取项目场景详情 - GET
/api/v1/projects/{project_id}/locations- 获取项目场景列表 - PUT
/api/v1/projects/{project_id}/locations/{location_id}- 更新项目场景 - DELETE
/api/v1/projects/{project_id}/locations/{location_id}- 删除项目场景 - POST
/api/v1/projects/{project_id}/props- 创建项目道具 - GET
/api/v1/projects/{project_id}/props/{prop_id}- 获取项目道具详情 - GET
/api/v1/projects/{project_id}/props- 获取项目道具列表 - PUT
/api/v1/projects/{project_id}/props/{prop_id}- 更新项目道具 - DELETE
/api/v1/projects/{project_id}/props/{prop_id}- 删除项目道具
project_element_tags.py(6 个接口)
- POST
/api/v1/projects/{project_id}/element-tags- 创建项目元素标签 - GET
/api/v1/projects/{project_id}/element-tags/element/{element_type}/{element_id}- 获取元素的所有标签 - GET
/api/v1/projects/{project_id}/element-tags- 获取项目的所有标签 - PUT
/api/v1/projects/{project_id}/element-tags/{tag_id}- 更新项目元素标签 - DELETE
/api/v1/projects/{project_id}/element-tags/{tag_id}- 删除项目元素标签 - PUT
/api/v1/projects/{project_id}/elements/{element_type}/{element_id}/default-tag- 设置元素的默认标签
迁移模式
# 修改前
from app.schemas.response import ApiResponse, success_response
@router.get("", response_model=ApiResponse[ProjectListResponse])
async def get_projects():
result = await service.get_projects()
return success_response(data=result)
# 修改后
from app.schemas.common import SuccessResponse
@router.get("", response_model=SuccessResponse[ProjectListResponse])
async def get_projects():
result = await service.get_projects()
return SuccessResponse(data=result)
查询参数 camelCase alias
为以下参数添加了 alias:
# project_resources.py
element_tag_id: Optional[str] = Query(None, alias="elementTagId")
page_size: int = Query(20, ge=1, le=100, alias="pageSize")
验证结果
✅ server/app/api/v1/projects.py: No diagnostics found
✅ server/app/api/v1/project_resources.py: No diagnostics found
✅ server/app/api/v1/project_elements.py: No diagnostics found
✅ server/app/api/v1/project_element_tags.py: No diagnostics found
更新统计
- 总迁移文件数: 13(storyboards, storyboard_resources, storyboard_board, screenplays, ai_models, ai_prompts, ai_conversations, ai, ai_jobs, projects, project_resources, project_elements, project_element_tags)
- 总迁移接口数: 136(93 + 43)
- 剩余待迁移模块: 5 个(resource_library, folders, auth, recharge, wechat 等)
技术收益
- 项目模块完全统一:所有项目相关接口使用标准响应格式
- 前端开发体验提升:查询参数支持 camelCase,符合前端命名习惯
- 代码简洁性:统一的返回模式,减少冗余代码
- 类型安全性:充分利用 Pydantic 类型系统
- API 文档质量:OpenAPI 文档类型更明确