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.
18 KiB
18 KiB
项目素材服务 - 流程图与关系图
文档版本:v1.0
最后更新:2026-02-02
关联文档:项目素材服务
目录
素材上传流程
sequenceDiagram
participant U as 用户
participant F as 前端
participant A as API 服务
participant PS as ProjectService
participant PRS as ProjectResourceService
participant FS as FileStorageService
participant DB as 数据库
participant S3 as 对象存储
U->>F: 上传素材文件
F->>F: 选择素材类型(character/scene/prop/footage)
F->>F: 可选:关联标签(element_tag_id)
F->>A: POST /api/v1/projects/{id}/resources
A->>PRS: upload_resource()
PRS->>PS: 检查项目存在性和权限
PS->>DB: 查询项目信息
DB-->>PS: 返回项目数据
PS-->>PRS: 验证通过
alt 素材类型为 footage
PRS->>PRS: 检查文件类型(图片或视频)
alt 文件为视频
PRS->>PRS: 验证视频大小(<= 500MB)
PRS->>PRS: 使用 ffmpeg 获取视频元数据
else 文件为图片
PRS->>PRS: 验证图片大小(<= 20MB)
PRS->>PRS: 获取图片尺寸
end
else 其他素材类型
PRS->>PRS: 验证图片类型和大小(<= 20MB)
PRS->>PRS: 获取图片尺寸
end
PRS->>FS: upload_file()
FS->>FS: 计算文件 SHA256 校验和
FS->>DB: 检查文件是否已存在(去重)
alt 文件已存在
FS->>DB: 增加引用计数
else 文件不存在
FS->>S3: 上传文件到对象存储
S3-->>FS: 返回文件 URL
FS->>DB: 创建 file_checksums 记录
end
FS-->>PRS: 返回文件元数据
alt 文件为视频
PRS->>PRS: 使用 ffmpeg 提取第一帧
PRS->>FS: 上传视频缩略图
FS->>S3: 上传缩略图
S3-->>FS: 返回缩略图 URL
FS-->>PRS: 返回缩略图元数据
else 文件为图片
PRS->>PRS: 生成 300x300 缩略图
PRS->>FS: 上传图片缩略图
FS->>S3: 上传缩略图
S3-->>FS: 返回缩略图 URL
FS-->>PRS: 返回缩略图元数据
end
alt 提供了 element_tag_id
PRS->>DB: 查询标签信息
DB-->>PRS: 返回 element_name 和 tag_label
PRS->>PRS: 填充冗余字段
end
PRS->>PRS: 生成 UUID v7
PRS->>DB: 创建 project_resources 记录
DB-->>PRS: 返回素材信息
PRS-->>A: 返回素材数据
A-->>F: 返回成功响应
F-->>U: 显示上传成功
流程说明:
- 项目权限检查:确保用户有权限在项目中上传素材
- 文件类型验证:
character/scene/prop:仅支持图片(JPEG、PNG、GIF、WebP、BMP),最大 20MBfootage:支持图片和视频(MP4、MOV、AVI、WebM),图片最大 20MB,视频最大 500MB
- 文件去重:通过 SHA256 校验和检查文件是否已存在
- 缩略图生成:
- 图片:生成 300x300 缩略图
- 视频:使用 ffmpeg 提取第一帧作为缩略图
- 标签关联:如果提供
element_tag_id,自动填充element_name和tag_label冗余字段 - UUID v7 生成:应用层生成主键
AI 生成素材流程
sequenceDiagram
participant U as 用户
participant F as 前端
participant A as API 服务
participant AI as AI Service
participant Q as Celery 队列
participant W as Worker
participant PRS as ProjectResourceService
participant FS as FileStorageService
participant DB as 数据库
participant S3 as 对象存储
U->>F: 输入提示词生成素材
F->>A: POST /api/v1/ai/generate-image
A->>AI: 创建 AI 任务
AI->>DB: 检查积分余额
AI->>DB: 创建 AI 任务记录
AI->>DB: 扣除积分
AI->>Q: 提交异步任务
AI-->>A: 返回任务 ID
A-->>F: 返回任务 ID
F-->>U: 显示生成中
Q->>W: 分配任务
W->>W: 调用 AI 大模型 API
W->>W: 获取生成的图片数据
W->>PRS: create_ai_generated_resource()
PRS->>PRS: 解析图片格式和尺寸
PRS->>FS: upload_file()
FS->>FS: 计算 SHA256 校验和
FS->>S3: 上传图片
S3-->>FS: 返回文件 URL
FS->>DB: 创建 file_checksums 记录
FS-->>PRS: 返回文件元数据
PRS->>PRS: 生成 300x300 缩略图
PRS->>FS: 上传缩略图
FS->>S3: 上传缩略图
S3-->>FS: 返回缩略图 URL
FS-->>PRS: 返回缩略图元数据
alt 提供了 element_tag_id
PRS->>DB: 查询标签信息
DB-->>PRS: 返回 element_name 和 tag_label
PRS->>PRS: 填充冗余字段
end
PRS->>PRS: 生成 UUID v7
PRS->>DB: 创建 project_resources 记录
PRS->>DB: 关联 ai_job_id
DB-->>PRS: 返回素材信息
W->>DB: 更新 AI 任务状态(completed)
loop 轮询任务状态
F->>A: GET /api/v1/ai/jobs/{taskId}
A->>DB: 查询任务状态
alt 任务完成
DB-->>A: 返回素材信息
A-->>F: 返回素材数据
F->>F: 显示生成结果
F-->>U: 显示素材图片
end
end
流程说明:
- 积分检查:AI Service 检查用户积分余额
- 异步任务:创建 Celery 任务,立即返回 task_id
- AI 生成:Worker 调用 AI 大模型 API 生成图片
- 文件处理:与上传流程类似,计算校验和、生成缩略图
- 任务关联:记录
ai_job_id,便于追溯生成历史 - 轮询状态:前端轮询任务状态,完成后显示结果
素材删除流程
sequenceDiagram
participant U as 用户
participant F as 前端
participant A as API 服务
participant PRS as ProjectResourceService
participant FS as FileStorageService
participant DB as 数据库
U->>F: 点击删除素材
F->>A: GET /api/v1/resources/{id}/usage
A->>PRS: check_resource_usage()
PRS->>DB: 查询 usage_count
DB-->>PRS: 返回使用情况
PRS-->>A: 返回使用统计
A-->>F: 返回 usage_count
alt usage_count > 0
F->>F: 显示警告对话框
F-->>U: 提示"素材正在被 N 个分镜使用"
U->>F: 选择是否强制删除
alt 用户取消
F-->>U: 取消删除
else 用户确认强制删除
F->>A: DELETE /api/v1/resources/{id}?force=true
end
else usage_count = 0
F->>F: 显示确认对话框
U->>F: 确认删除
F->>A: DELETE /api/v1/resources/{id}?force=false
end
A->>PRS: delete_resource()
PRS->>DB: 检查项目权限
alt force=false 且 usage_count > 0
PRS-->>A: 抛出 ValidationError
A-->>F: 返回 400 错误
F-->>U: 显示错误提示
else 允许删除
PRS->>DB: 软删除(设置 deleted_at)
PRS->>FS: decrease_reference_count()
FS->>DB: 减少文件引用计数
alt 引用计数为 0
FS->>DB: 标记文件可清理
Note over FS: 定时任务会清理无引用文件
end
FS-->>PRS: 返回成功
PRS-->>A: 返回成功
A-->>F: 返回成功响应
F->>F: 从列表中移除素材
F-->>U: 显示删除成功
end
流程说明:
- 使用检查:删除前检查
usage_count(被多少个分镜使用) - 用户确认:
usage_count > 0:显示警告,询问是否强制删除usage_count = 0:直接确认删除
- 删除保护:默认禁止删除正在使用的素材(
force=false) - 强制删除:
force=true时允许删除,但会导致分镜失去素材引用 - 软删除:设置
deleted_at,不物理删除记录 - 引用计数:减少文件引用计数,引用为 0 时标记可清理
素材与分镜关联流程
sequenceDiagram
participant U as 用户
participant F as 前端
participant A as API 服务
participant SRS as StoryboardResourceService
participant PRS as ProjectResourceService
participant DB as 数据库
U->>F: 将素材拖拽到分镜
F->>A: POST /api/v1/storyboards/{id}/resources
A->>SRS: add_resource_to_storyboard()
SRS->>DB: 检查分镜存在性
SRS->>PRS: 检查素材存在性
PRS->>DB: 查询素材信息
DB-->>PRS: 返回素材数据
PRS-->>SRS: 验证通过
SRS->>DB: 检查是否已关联
alt 已关联
SRS-->>A: 返回错误
A-->>F: 返回 400 错误
F-->>U: 提示"素材已添加"
else 未关联
SRS->>DB: 创建 storyboard_resources 记录
SRS->>DB: 增加素材 usage_count
DB-->>SRS: 返回关联信息
SRS-->>A: 返回成功
A-->>F: 返回关联数据
F->>F: 更新分镜资源列表
F-->>U: 显示素材已添加
end
Note over U,DB: 从分镜移除素材
U->>F: 点击移除素材
F->>A: DELETE /api/v1/storyboards/{id}/resources/{resourceId}
A->>SRS: remove_resource_from_storyboard()
SRS->>DB: 删除 storyboard_resources 记录
SRS->>DB: 减少素材 usage_count
DB-->>SRS: 返回成功
SRS-->>A: 返回成功
A-->>F: 返回成功响应
F->>F: 从列表中移除素材
F-->>U: 显示移除成功
流程说明:
- 添加素材到分镜:
- 检查分镜和素材存在性
- 检查是否已关联(避免重复)
- 创建
storyboard_resources关联记录 - 增加素材的
usage_count
- 从分镜移除素材:
- 删除
storyboard_resources关联记录 - 减少素材的
usage_count
- 删除
- 引用计数维护:
usage_count由StoryboardResourceService自动维护
数据库表关系图
erDiagram
projects ||--o{ project_resources : "归属"
users ||--o{ project_resources : "创建"
screenplay_element_tags ||--o{ project_resources : "关联标签"
ai_jobs ||--o{ project_resources : "AI生成"
resources ||--o{ project_resources : "来源-后期扩展"
file_checksums ||--o{ project_resources : "文件去重"
project_resources ||--o{ storyboard_resources : "关联"
storyboards ||--o{ storyboard_resources : "使用"
projects {
uuid project_id PK "项目ID"
text name "项目名称"
uuid created_by FK "创建者"
}
project_resources {
uuid project_resource_id PK "素材ID-UUID_v7"
uuid project_id FK "项目ID-逻辑外键"
text name "素材名称"
smallint type "素材类型-1到4"
text file_url "文件URL"
text thumbnail_url "缩略图URL"
bigint file_size "文件大小"
text checksum "文件校验和"
uuid element_tag_id FK "标签ID-逻辑外键"
text element_name "元素名称-冗余"
text tag_label "标签名称-冗余"
uuid ai_job_id FK "AI任务ID-逻辑外键"
jsonb meta_data "元数据"
int usage_count "引用计数"
uuid created_by FK "创建者-逻辑外键"
timestamptz created_at "创建时间"
timestamptz deleted_at "删除时间"
}
storyboard_resources {
uuid storyboard_resource_id PK "关联ID-UUID_v7"
uuid storyboard_id FK "分镜ID-逻辑外键"
uuid project_resource_id FK "素材ID-逻辑外键"
smallint resource_type "素材类型-1到4"
int display_order "显示顺序"
timestamptz created_at "创建时间"
}
screenplay_element_tags {
uuid element_tag_id PK "标签ID"
text element_name "元素名称"
text tag_label "标签名称"
}
file_checksums {
text checksum PK "文件校验和"
text file_url "文件URL"
int reference_count "引用计数"
}
ai_jobs {
uuid ai_job_id PK "AI任务ID"
text status "任务状态"
}
users {
uuid user_id PK "用户ID"
text nickname "昵称"
}
storyboards {
uuid storyboard_id PK "分镜ID"
uuid project_id FK "项目ID"
text title "分镜标题"
}
resources {
uuid resource_id PK "资源ID"
text name "资源名称"
}
说明:
- 所有外键关系均为逻辑外键(无物理约束)
- 引用完整性由应用层(Service/Repository)保证
element_name和tag_label为冗余字段,优化查询性能usage_count记录素材被多少个分镜使用file_checksums表实现文件去重
素材类型枚举
graph LR
ResourceType[素材类型 SMALLINT] --> Character[1 = character 角色]
ResourceType --> Scene[2 = scene 场景]
ResourceType --> Prop[3 = prop 道具]
ResourceType --> Footage[4 = footage 实拍]
Character --> CharDesc[主角、配角、群演形象]
Scene --> SceneDesc[室内外场景背景]
Prop --> PropDesc[物品、工具、装饰]
Footage --> FootageDesc[航拍、瞰景、实拍视频]
Character --> AISupport1[✅ 支持 AI 生成]
Scene --> AISupport2[✅ 支持 AI 生成]
Prop --> AISupport3[✅ 支持 AI 生成]
Footage --> AISupport4[❌ 不支持 AI 生成]
Character --> TagSupport1[✅ 可关联标签]
Scene --> TagSupport2[✅ 可关联标签]
Prop --> TagSupport3[✅ 可关联标签]
Footage --> TagSupport4[❌ 不可关联标签]
style ResourceType fill:#e1f5e1
style Footage fill:#ffe1e1
素材类型说明:
| 类型 | 数值 | 字符串 | 描述 | AI 生成 | 标签关联 | 文件类型 |
|---|---|---|---|---|---|---|
| 角色 | 1 | character | 主角、配角、群演形象 | ✅ | ✅ | 图片 |
| 场景 | 2 | scene | 室内外场景背景 | ✅ | ✅ | 图片 |
| 道具 | 3 | prop | 物品、工具、装饰 | ✅ | ✅ | 图片 |
| 实拍 | 4 | footage | 航拍、瞰景、实拍视频 | ❌ | ❌ | 图片/视频 |
数据库约束:
-- 实拍素材不能有标签关联
CONSTRAINT project_resources_footage_no_tag_check CHECK (
(type = 4 AND element_tag_id IS NULL AND element_name IS NULL AND tag_label IS NULL) OR
(type IN (1, 2, 3))
)
服务依赖关系图
graph TB
API[API 路由层] --> PRS[ProjectResourceService]
PRS --> PS[ProjectService]
PRS --> FS[FileStorageService]
PRS --> Repo[ProjectResourceRepository]
PS --> DB[(PostgreSQL)]
FS --> DB
FS --> S3[对象存储 MinIO/S3]
Repo --> DB
PRS --> TagRepo[ScreenplayTagRepository]
PRS --> ScreenplayRepo[ScreenplayRepository]
TagRepo --> DB
ScreenplayRepo --> DB
SRS[StoryboardResourceService] --> PRS
SRS --> DB
AI[AI Service] --> PRS
AI --> Queue[Celery 队列]
Queue --> Worker[Worker]
Worker --> PRS
style PRS fill:#e1f5e1
style FS fill:#fff4e1
style S3 fill:#e1f0ff
依赖说明:
-
ProjectResourceService(核心服务):
- 依赖
ProjectService:检查项目权限 - 依赖
FileStorageService:文件上传和去重 - 依赖
ScreenplayTagRepository:验证标签关联 - 依赖
ScreenplayRepository:验证标签归属项目
- 依赖
-
StoryboardResourceService:
- 调用
ProjectResourceService检查素材存在性 - 维护素材的
usage_count
- 调用
-
AI Service:
- 通过 Celery Worker 调用
ProjectResourceService.create_ai_generated_resource() - 异步生成素材
- 通过 Celery Worker 调用
-
FileStorageService:
- 上传文件到对象存储(MinIO/S3)
- 管理文件去重(
file_checksums表)
文件去重机制
graph TB
Upload[上传文件] --> CalcHash[计算 SHA256 校验和]
CalcHash --> CheckDB{文件已存在?}
CheckDB -->|是| IncRef[增加引用计数]
CheckDB -->|否| UploadS3[上传到 S3]
UploadS3 --> CreateRecord[创建 file_checksums 记录]
CreateRecord --> SetRef[设置引用计数 = 1]
IncRef --> CreateResource[创建 project_resources 记录]
SetRef --> CreateResource
CreateResource --> ReuseURL[复用已有文件 URL]
Delete[删除素材] --> DecRef[减少引用计数]
DecRef --> CheckRef{引用计数 = 0?}
CheckRef -->|是| MarkClean[标记文件可清理]
CheckRef -->|否| Keep[保留文件]
MarkClean --> CronJob[定时任务清理]
CronJob --> DeleteS3[从 S3 删除文件]
style CheckDB fill:#fff4e1
style CheckRef fill:#fff4e1
style UploadS3 fill:#e1f0ff
去重机制说明:
-
上传阶段:
- 计算文件的 SHA256 校验和
- 查询
file_checksums表检查文件是否已存在 - 如果存在:增加引用计数,复用已有 URL
- 如果不存在:上传到 S3,创建新记录
-
删除阶段:
- 软删除
project_resources记录 - 减少
file_checksums的引用计数 - 引用计数为 0 时,标记文件可清理
- 软删除
-
清理阶段:
- 定时任务扫描引用计数为 0 的文件
- 从 S3 删除物理文件
- 删除
file_checksums记录
优点:
- 节省存储空间(相同文件只存储一份)
- 加快上传速度(已存在的文件秒传)
- 安全删除(引用计数为 0 才物理删除)
相关文档
文档版本:v1.0
最后更新:2026-02-02