# 项目资源服务实现总结 **日期**: 2026-02-02 **类型**: 功能实现 **影响范围**: 后端 - 项目资源管理、子项目功能 ## 概述 完整实现了项目资源服务(ProjectResourceService)和子项目功能,支持项目专属素材管理(角色、场景、道具、实拍)和分镜素材关联。 ## 实现内容 ### 1. 数据模型 #### ProjectResource 模型 (`server/app/models/project_resource.py`) - 支持 4 种素材类型:角色(1)、场景(2)、道具(3)、实拍(4) - 文件信息:URL、缩略图、尺寸、MIME 类型、校验和 - 标签关联:`element_tag_id`、`element_name`、`tag_label`(冗余字段) - AI 生成:`ai_job_id`、`extra_data`(JSONB) - 使用统计:`usage_count`(引用计数) - 软删除:`deleted_at` #### StoryboardResource 关联模型 - 多对多关联:分镜 ↔ 素材 - 类型标记:`resource_type`(冗余存储) - 自动维护 `usage_count` #### Project 模型扩展 - `parent_project_id`: 父项目 ID(子项目功能) - `screenplay_id`: 关联剧本 ID - `is_subproject` 属性:判断是否为子项目 ### 2. 仓储层 #### ProjectResourceRepository (`server/app/repositories/project_resource_repository.py`) - `create()`: 创建素材 - `get_by_id()`: 获取单个素材 - `get_by_project()`: 获取项目素材列表(支持类型、标签、搜索过滤) - `update()`: 更新素材 - `delete()`: 软删除素材 - `get_by_checksum()`: 通过校验和查找(去重) - `increment_usage()` / `decrement_usage()`: 维护使用计数 #### ProjectRepository 扩展 - `create_subproject()`: 创建子项目 - `get_subprojects()`: 获取子项目列表 - `count_subprojects()`: 统计子项目数量 ### 3. Schema 定义 #### 请求 Schema (`server/app/schemas/project_resource.py`) - `ProjectResourceCreate`: 创建素材(上传) - `ProjectResourceUpdate`: 更新素材 - `AIGenerateRequest`: AI 生成素材请求 #### 响应 Schema - `ProjectResourceResponse`: 素材详情 - `ProjectResourceListResponse`: 素材列表(分页) - `ResourceUsageResponse`: 使用统计 ### 4. 服务层 #### ProjectResourceService (`server/app/services/project_resource_service.py`) - **素材上传**: - `upload_image()`: 上传图片素材 - `upload_video()`: 上传视频素材(支持 ffmpeg 处理) - 自动生成缩略图 - 文件去重(基于 SHA256) - **AI 生成**: - `generate_resource()`: AI 生成素材 - 支持角色、场景、道具生成 - 自动关联 AI 任务 - **素材管理**: - `get_resource()`: 获取素材详情 - `list_resources()`: 获取素材列表(支持过滤、搜索、分页) - `update_resource()`: 更新素材 - `delete_resource()`: 删除素材 - `get_usage_stats()`: 获取使用统计 - **权限检查**: - `_check_project_permission()`: 检查项目权限 #### StoryboardResourceService (`server/app/services/storyboard_resource_service.py`) - `add_resource_to_storyboard()`: 添加素材到分镜 - `remove_resource_from_storyboard()`: 从分镜移除素材 - `get_storyboard_resources()`: 获取分镜的所有素材 - `get_resource_storyboards()`: 获取素材关联的分镜 - `batch_add_resources()`: 批量添加素材 - `batch_remove_resources()`: 批量移除素材 - 自动维护 `usage_count` 字段 #### ProjectService 扩展 - `create_subproject()`: 创建子项目 - `get_subprojects()`: 获取子项目列表(分页) ### 5. API 路由 #### ProjectResourcesRouter (`server/app/api/v1/project_resources.py`) ``` POST /api/v1/projects/{project_id}/resources/upload/image # 上传图片 POST /api/v1/projects/{project_id}/resources/upload/video # 上传视频 POST /api/v1/projects/{project_id}/resources/generate # AI 生成 GET /api/v1/projects/{project_id}/resources # 获取列表 GET /api/v1/projects/{project_id}/resources/{resource_id} # 获取详情 PUT /api/v1/projects/{project_id}/resources/{resource_id} # 更新素材 DELETE /api/v1/projects/{project_id}/resources/{resource_id} # 删除素材 GET /api/v1/projects/{project_id}/resources/{resource_id}/usage # 使用统计 ``` #### ProjectsRouter 扩展 (`server/app/api/v1/projects.py`) ``` GET /api/v1/projects/{parent_project_id}/subprojects # 获取子项目列表 ``` ### 6. 数据库迁移 #### 迁移脚本 (`server/alembic/versions/20260202_1600_add_project_resources_and_subprojects.py`) **project_resources 表**: - 主键:`project_resource_id` (UUID v7) - 索引: - `idx_project_resources_project_id` (project_id, WHERE deleted_at IS NULL) - `idx_project_resources_type` (type, WHERE deleted_at IS NULL) - `idx_project_resources_created_by` (created_by, WHERE deleted_at IS NULL) - `idx_project_resources_checksum` (checksum) - 去重 - `idx_project_resources_element_tag_id` (element_tag_id, WHERE NOT NULL) - `idx_project_resources_ai_job_id` (ai_job_id, WHERE NOT NULL) - `idx_project_resources_usage_count` (usage_count, WHERE deleted_at IS NULL) - `idx_project_resources_extra_data_gin` (extra_data) - GIN 索引 - `idx_project_resources_name_trgm` (name) - 全文搜索 - `idx_project_resources_element_name_trgm` (element_name) - 全文搜索 - `idx_project_resources_tag_label_trgm` (tag_label) - 全文搜索 - 约束: - `usage_count >= 0` - 实拍素材不能有标签关联 **storyboard_resources 表**: - 主键:`id` (UUID v7) - 索引: - `idx_storyboard_resources_storyboard_id` - `idx_storyboard_resources_project_resource_id` - `idx_storyboard_resources_type` - 唯一约束:`(storyboard_id, project_resource_id)` **projects 表扩展**: - 新增字段: - `parent_project_id` (UUID, nullable) - `screenplay_id` (UUID, nullable) - 索引: - `idx_projects_parent_project_id` - `idx_projects_screenplay_id` - 约束:子项目必须有 `screenplay_id` ### 7. Docker 配置 #### Dockerfile 更新 (`server/Dockerfile`) - 添加 `ffmpeg` 依赖(视频处理) ## 技术亮点 ### 1. 文件去重 - 基于 SHA256 校验和 - 避免重复上传相同文件 - 节省存储空间 ### 2. 使用计数维护 - 自动维护 `usage_count` 字段 - 通过 `StoryboardResourceService` 统一管理 - 支持批量操作 ### 3. 冗余字段设计 - `element_name`、`tag_label` 冗余存储 - 减少 JOIN 查询 - 提升列表查询性能 ### 4. 全文搜索 - 使用 pg_trgm 扩展 - 支持模糊搜索素材名称、元素名称、标签 - GIN 索引优化 JSONB 查询 ### 5. 权限控制 - 继承项目权限 - 支持 viewer/editor/owner 角色 - 应用层校验(无物理外键) ### 6. 软删除 - `deleted_at` 字段 - 索引过滤 `WHERE deleted_at IS NULL` - 保留历史数据 ## 数据流程 ### 上传素材流程 ``` 1. 用户上传文件 → API 接收 2. 计算 SHA256 校验和 3. 检查是否已存在(去重) 4. 上传到文件存储服务 5. 生成缩略图(图片/视频) 6. 创建 ProjectResource 记录 7. 返回素材信息 ``` ### AI 生成素材流程 ``` 1. 用户提交生成请求 → API 接收 2. 检查项目权限 3. 调用 AI 服务生成素材 4. 下载生成的文件 5. 上传到文件存储 6. 创建 ProjectResource 记录(关联 ai_job_id) 7. 返回素材信息 ``` ### 分镜关联素材流程 ``` 1. 用户添加素材到分镜 2. 创建 StoryboardResource 关联 3. 增加 ProjectResource.usage_count 4. 返回成功 移除时: 1. 删除 StoryboardResource 关联 2. 减少 ProjectResource.usage_count 3. 返回成功 ``` ## 待实现功能 ### 1. 分镜素材关联 API - 需要在 `storyboards` 路由中添加: - `POST /api/v1/storyboards/{storyboard_id}/resources` - 添加素材 - `DELETE /api/v1/storyboards/{storyboard_id}/resources/{resource_id}` - 移除素材 - `GET /api/v1/storyboards/{storyboard_id}/resources` - 获取素材列表 ### 2. 视频处理优化 - 当前为同步处理,大文件可能超时 - 建议改为异步任务(Celery) - 支持进度查询 ### 3. 缩略图生成优化 - 支持多种尺寸 - 支持自定义裁剪 - 异步生成 ### 4. 素材库集成 - 从素材库导入到项目 - 支持批量导入 - 自动关联标签 ### 5. 测试 - 单元测试 - 集成测试 - API 测试 ## 注意事项 ### 1. 字段命名变更 - ⚠️ 原设计中的 `metadata` 字段改为 `extra_data` - 原因:`metadata` 是 SQLAlchemy 保留字段名 - 影响:迁移脚本、模型定义、索引名称 ### 2. ffmpeg 依赖 - 需要重新构建 Docker 镜像: ```bash docker compose build app docker compose up -d app ``` ### 3. 权限检查 - 所有操作都需要检查项目权限 - 使用 `ProjectService._check_permission()` 方法 - 支持 viewer/editor/owner 角色 ### 4. 文件存储 - 依赖 `FileStorageService` - 支持本地存储和 S3 - 自动生成唯一文件名 ## 相关文档 - 需求文档:`docs/requirements/backend/04-services/project/project-resource-service.md` - 项目服务文档:`docs/requirements/backend/04-services/project/project-service.md` - 数据库设计:`docs/requirements/database-design.md` - API 设计:`docs/requirements/api-design-specification.md` ## 验证清单 - [x] 数据库迁移成功执行 - [x] API 路由注册成功 - [x] 模型定义正确 - [x] 仓储层实现完整 - [x] 服务层实现完整 - [x] Schema 定义完整 - [x] 子项目功能实现 - [x] 分镜素材关联服务实现 - [ ] ffmpeg 依赖安装(需要重新构建镜像) - [ ] API 端点测试 - [ ] 权限检查测试 - [ ] 文件上传测试 - [ ] AI 生成测试 ## 下一步 1. **重新构建 Docker 镜像**(安装 ffmpeg) 2. **实现分镜素材关联 API** 3. **编写测试用例** 4. **性能优化**(异步任务、缓存) 5. **前端集成**