# 修复项目素材 API 文件 URL 返回问题 **日期**: 2026-02-12 **类型**: Bug 修复 **影响范围**: 后端 API - 项目素材模块 ## 问题描述 `/api/v1/projects/{project_id}/resources` 接口上传 footage(实拍)素材后,返回的 `fileUrl` 和 `thumbnailUrl` 字段为相对路径(如 `project_resource_footage/xxx.mp4`),而非完整的访问 URL(如 `https://oss.example.com/project_resource_footage/xxx.mp4`)。 前端无法直接使用相对路径访问文件,导致素材预览失败。 ## 根因分析 1. `ProjectResourceResponse` Schema 直接返回数据库存储的相对路径 2. 未使用 `build_file_url()` 函数转换为完整 URL 3. 其他模块(如 `ScreenplayResponse`、`AttachmentResponse`)已使用 `@computed_field` 自动转换 ## 解决方案 在 `ProjectResourceResponse` Schema 中添加 `@computed_field`,自动将相对路径转换为完整 URL: ```python from pydantic import computed_field from app.core.storage import build_file_url class ProjectResourceResponse(BaseModel): # 内部字段存储相对路径 file_url: str = Field(..., description="文件相对路径(内部使用)") thumbnail_url: Optional[str] = Field(None, description="缩略图相对路径(内部使用)") # 计算字段返回完整 URL @computed_field(alias="fileUrl") @property def full_file_url(self) -> str: """动态生成完整文件访问 URL""" return build_file_url(self.file_url) @computed_field(alias="thumbnailUrl") @property def full_thumbnail_url(self) -> Optional[str]: """动态生成完整缩略图访问 URL""" return build_file_url(self.thumbnail_url) if self.thumbnail_url else None ``` ## 技术细节 ### 修改文件 - `server/app/schemas/project_resource.py` ### 工作原理 1. 数据库存储相对路径(便于域名变更和多环境部署) 2. Schema 序列化时自动调用 `build_file_url()` 转换为完整 URL 3. API 响应中 `fileUrl` 和 `thumbnailUrl` 字段返回完整 URL ### 优势 - **统一模式**: 与 `ScreenplayResponse`、`AttachmentResponse` 保持一致 - **自动转换**: 所有使用该 Schema 的接口自动生效 - **易维护**: 域名变更时只需修改配置,无需改代码 ## 影响范围 ### 受影响接口 - `POST /api/v1/projects/{project_id}/resources` - 上传素材 - `GET /api/v1/resources/{resource_id}` - 获取素材详情 - `PATCH /api/v1/resources/{resource_id}` - 更新素材 - `GET /api/v1/projects/{project_id}/resources` - 获取项目素材列表 - `GET /api/v1/projects/{project_id}/resources?element_tag_id={tag_id}` - 获取标签素材列表 ### 兼容性 - ✅ 向后兼容:前端已期望接收完整 URL - ✅ 无需数据迁移:数据库继续存储相对路径 - ✅ 无需前端改动:返回格式符合前端预期 ## 验证方法 ```bash # 1. 上传 footage 素材 curl -X POST "http://localhost:8000/api/v1/projects/{project_id}/resources" \ -H "Authorization: Bearer {token}" \ -F "file=@test.mp4" \ -F "name=测试视频" \ -F "type=footage" # 2. 检查响应中的 fileUrl 字段 # ✅ 正确: "fileUrl": "https://oss.example.com/project_resource_footage/xxx.mp4" # ❌ 错误: "fileUrl": "project_resource_footage/xxx.mp4" ``` ## 相关文件 - Schema: `server/app/schemas/project_resource.py` - API: `server/app/api/v1/project_resources.py` - 工具函数: `server/app/core/storage.py` (`build_file_url`) ## 参考 - 类似实现: `server/app/schemas/screenplay.py` (ScreenplayResponse) - 类似实现: `server/app/schemas/attachment.py` (AttachmentResponse)