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.
6.0 KiB
6.0 KiB
资源详情API与预览面板集成
日期: 2026-02-08
类型: 功能实现
影响范围: 后端API + 前端预览面板
概述
实现了项目资源(角色/场景/道具)的详情API,并集成到预览面板中,支持展示资源的标签列表及其关联图片。
背景
用户在资源面板点击角色/场景/道具时,需要在预览面板中查看:
- 资源的基础信息
- 资源的变体标签列表(如角色的"便装"、"正装")
- 每个标签关联的图片资源作为缩略图
之前只有列表API(/resource-library/*),无法单独获取某个资源的完整详情。
实施方案
后端改动
1. Service层新增方法(resource_library_service.py)
async def get_character_detail(user_id, project_id, character_id)
async def get_location_detail(user_id, project_id, location_id)
async def get_prop_detail(user_id, project_id, prop_id)
逻辑:
- 复用现有的
_build_*_with_resources()方法 - 查询
project_element_tags表获取标签 - 通过
project_resources.element_tag_id关联获取每个标签的图片资源 - 返回标签列表,每个标签包含:
tag_idtag_labelresource_count: 关联的图片数量(动态计算)thumbnail_url: 第一张图片的缩略图URL
2. API层新增路由(project_elements.py)
GET /api/v1/projects/{project_id}/characters/{character_id}
GET /api/v1/projects/{project_id}/locations/{location_id}
GET /api/v1/projects/{project_id}/props/{prop_id}
响应示例:
{
"data": {
"character_id": "xxx",
"name": "孙悟空",
"has_tags": true,
"tags": [
{
"tag_id": "tag-1",
"tag_label": "便装",
"description": "日常装扮",
"display_order": 0,
"resource_count": 3,
"thumbnail_url": "https://..."
}
]
}
}
前端改动
1. API Client 扩展(project-elements.ts)
新增类型定义和API方法:
export interface CharacterDetailResponse {
character_id: string;
name: string;
tags: ElementTag[];
// ...
}
projectElementsApi.getCharacterDetail(projectId, characterId)
projectElementsApi.getLocationDetail(projectId, locationId)
projectElementsApi.getPropDetail(projectId, propId)
2. 新增 Hook(useProjectElementsDetail.ts)
useCharacterDetail(projectId, characterId)
useLocationDetail(projectId, locationId)
usePropDetail(projectId, propId)
3. 修改预览数据Hook(usePreviewData.ts)
变更前:
- 使用资源库列表API获取数据
- 从
metadata.tags读取标签(mock数据)
变更后:
- 根据资源类型调用对应的详情API
- 直接使用API返回的真实标签数据
- 格式化为统一的
resource对象供预览面板使用
const { data: characterDetail } = useCharacterDetail(projectId, characterId);
const resource = {
id: characterDetail.character_id,
metadata: {
tags: characterDetail.tags.map(tag => ({
tag_id: tag.tag_id,
tag_label: tag.tag_label,
resource_count: tag.resource_count,
thumbnail_url: tag.thumbnail_url,
}))
}
};
数据流
1. 用户点击资源面板的"孙悟空"角色
↓
2. ProjectResourcePanel 调用 selectResource('characterId', 'character')
↓
3. usePreviewData 根据 selectedResourceType 调用 useCharacterDetail
↓
4. 后端 GET /api/v1/projects/{projectId}/characters/{characterId}
↓
5. ResourceLibraryService.get_character_detail()
- 查询 project_element_tags 表
- 查询 project_resources 表(通过 element_tag_id)
- 构建标签列表(每个标签含 thumbnail_url)
↓
6. 前端接收数据,格式化为统一的 resource 对象
↓
7. PreviewPanel 渲染标签缩略图列表
技术细节
标签的 resource_count 计算
数据库层面:
project_element_tags表没有resource_count字段- 通过 SQL JOIN 动态计算:
resources = await project_resource_repo.get_by_element_tag_id(tag.tag_id) resource_count = len(resources)
标签的 thumbnail_url 来源
取标签关联的第一张图片的缩略图:
thumbnail_url = resources[0].thumbnail_url or resources[0].file_url
API路由规划
- 详情接口:
/api/v1/projects/{projectId}/characters/{characterId} - 列表接口:
/api/v1/projects/{projectId}/characters - 资源库接口(逐步废弃):
/api/v1/projects/{projectId}/resource-library/characters
未来计划用 /characters 替代 /resource-library/characters。
测试建议
-
后端测试:
curl -X GET "http://localhost:8000/api/v1/projects/{projectId}/characters/{characterId}" \ -H "Authorization: Bearer {token}" -
前端测试:
- 在资源面板点击角色/场景/道具
- 验证预览面板显示标签列表
- 验证每个标签显示正确的缩略图
后续扩展
-
新增标签功能(待实现):
- 调用
POST /api/v1/projects/{projectId}/element-tags - 刷新详情数据
- 调用
-
上传图片到标签(待实现):
- 调用
POST /api/v1/projects/{projectId}/resources - 传入
element_tag_id关联到标签
- 调用
-
AI生成标签图片(暂不实现):
- 根据标签描述生成图片
- 自动关联到标签
影响的文件
后端
server/app/services/resource_library_service.py- 新增3个详情方法server/app/api/v1/project_elements.py- 新增3个详情路由
前端
client/src/services/api/project-elements.ts- 新增详情API方法client/src/hooks/api/useProjectElementsDetail.ts- 新增详情Hooksclient/src/hooks/api/index.ts- 导出新Hooksclient/src/components/features/preview/hooks/usePreviewData.ts- 使用详情API
注意事项
- 兼容性:实拍资源(footage)仍使用资源库API,未来需统一
- 性能:详情API会查询标签和关联资源,数据量大时需优化
- 缓存:前端使用 React Query 缓存,5分钟有效期