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.
12 KiB
12 KiB
附件与资源管理架构方案
文档版本:v1.0
创建时间:2025-01-27
作者:系统架构师
目录
背景与问题
1.1 原始设计问题
在初始设计中,所有文件(文档、图片、视频、音频)都计划存储在单一的 attachments 表中,这会导致以下问题:
- 表过大风险:随着项目增多,单表可能达到百万级甚至千万级记录
- 业务混乱:角色/场景/道具素材与普通文档附件的使用场景完全不同
- 查询性能差:不同类型文件的查询模式不同,单表难以针对性优化
- 扩展困难:后期需要支持素材市场功能,单表设计难以扩展
1.2 核心需求
- 业务分层:区分"文档附件"、"项目素材"、"系统资源库"
- 性能保证:每个表的数据量控制在合理范围内
- 去重机制:相同文件只存储一次,节省存储空间
- 扩展性:支持后期素材市场功能扩展
架构设计
2.1 核心思想
业务分离 + 服务层统一:
- 不同业务场景的文件使用不同的表管理
- 通过公共服务层(FileStorageService)实现代码复用和去重
- 使用 file_checksums 表实现全局去重
2.2 架构图
┌─────────────────────────────────────────────────────────────┐
│ 业务层(Services) │
├─────────────────┬─────────────────┬─────────────────────────┤
│ AttachmentService│ProjectResource │ VideoService │
│ (文档附件) │Service(项目素材)│ (视频管理) │
└────────┬────────┴────────┬────────┴────────┬───────────────┘
│ │ │
└─────────────────┼─────────────────┘
│
┌──────────▼──────────┐
│ FileStorageService │ (公共服务层)
│ - 文件上传 │
│ - 文件去重 │
│ - 引用计数管理 │
└──────────┬──────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│file_ │ │Object │ │Database │
│checksums│ │Storage │ │Tables │
│(去重表) │ │(MinIO) │ │ │
└─────────┘ └─────────┘ └─────────┘
2.3 表结构分层
attachments 表
├─ 用途:文档类附件(剧本、合同、参考资料、头像、封面)
├─ 分类:document, image
├─ 关联:project_id, script_id
└─ 数据量:1-5万条(1000个项目)
project_resources 表
├─ 用途:项目专属素材(角色、场景、道具)
├─ 分类:character, scene, prop
├─ 关联:project_id
└─ 数据量:5-30万条(1000个项目)
resources 表(后期扩展)
├─ 用途:系统资源库 + 素材市场
├─ 特点:is_public, is_official, price
└─ 数据量:1-10万条(公共资源)
file_checksums 表
├─ 用途:全局文件去重
└─ 数据量:与文件总数相当
videos/sound_effects/voiceovers 表
├─ 用途:视频、音效、配音管理
└─ 继续独立管理
表结构对比
3.1 调整前 vs 调整后
attachments 表
调整前:
CREATE TABLE attachments (
attachment_id BIGINT PRIMARY KEY,
category ENUM('script', 'document', 'contract', 'reference', 'other'),
project_id BIGINT,
user_id BIGINT, -- 多态关联,语义不清
checksum TEXT, -- 可选
-- ...
);
调整后:
CREATE TABLE attachments (
attachment_id BIGINT PRIMARY KEY,
category ENUM('document', 'image'), -- 简化分类
project_id BIGINT,
script_id BIGINT, -- 新增剧本关联
checksum TEXT NOT NULL, -- 必填,支持去重
-- 移除 user_id(改为业务表存储)
-- ...
);
-- 业务表新增字段(一对一关联)
ALTER TABLE users ADD COLUMN avatar_id BIGINT REFERENCES attachments;
ALTER TABLE projects ADD COLUMN cover_image_id BIGINT REFERENCES attachments;
ALTER TABLE storyboards ADD COLUMN thumbnail_id BIGINT REFERENCES attachments;
resources 表
调整前:
CREATE TABLE resources (
resource_id BIGINT PRIMARY KEY,
type ENUM('character', 'scene', 'prop'),
file_url TEXT,
is_public BOOLEAN, -- 素材市场
price NUMERIC,
-- 项目和公共资源混在一起
);
调整后:
-- 拆分为两个表
-- 1. 项目专属素材(MVP 阶段实现)
CREATE TABLE project_resources (
project_resource_id BIGINT PRIMARY KEY,
project_id BIGINT NOT NULL, -- 归属项目
type ENUM('character', 'scene', 'prop'),
file_url TEXT NOT NULL,
checksum TEXT NOT NULL,
-- ...
);
-- 2. 系统资源库(后期扩展)
CREATE TABLE resources (
resource_id BIGINT PRIMARY KEY,
type ENUM('character', 'scene', 'prop'),
file_url TEXT NOT NULL,
is_public BOOLEAN, -- 素材市场
is_official BOOLEAN, -- 官方资源
price NUMERIC,
-- ...
);
3.2 新增表
file_checksums 表(全局去重)
CREATE TABLE file_checksums (
checksum TEXT PRIMARY KEY,
file_url TEXT NOT NULL,
file_size BIGINT NOT NULL,
mime_type TEXT NOT NULL,
storage_path TEXT NOT NULL,
reference_count INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL,
last_accessed_at TIMESTAMPTZ NOT NULL
);
作用:
- 跨所有表实现文件去重
- 记录引用计数,便于清理
- 记录最后访问时间,便于清理过期文件
资源流转路径
4.1 MVP 阶段(前期开发)
用户上传/AI生成
↓
项目素材库 (project_resources)
↓
分镜/时间轴 (storyboard_resources, timeline_items)
4.2 完整版(后期扩展)
系统资源库 (resources, is_official=true)
↓ 复制
素材市场 (resources, is_public=true)
↓ 下载/复制
项目素材库 (project_resources)
↓ 使用
分镜/时间轴 (storyboard_resources, timeline_items)
4.3 文件去重流程
用户上传文件
↓
计算 SHA256 校验和
↓
查询 file_checksums 表
├─ 存在 → 增加引用计数,返回已有 URL
└─ 不存在 → 上传到对象存储,记录到 file_checksums
数据量预估
5.1 单个项目数据量
| 类型 | 数量 | 说明 |
|---|---|---|
| 文档附件 | 10-50 | 剧本、合同、参考资料 |
| 项目素材 | 50-300 | 角色、场景、道具图片 |
| 视频片段 | 50-500 | 分镜视频、合成素材 |
| 音效 | 20-100 | 背景音乐、音效 |
| 配音 | 10-50 | 角色配音 |
| 总计 | 140-1000 | 单个项目文件总数 |
5.2 系统总数据量(1000个项目)
| 表名 | 数据量 | 说明 |
|---|---|---|
| attachments | 1-5万 | 文档附件 |
| project_resources | 5-30万 | 项目素材 |
| videos | 5-50万 | 视频 |
| sound_effects | 2-10万 | 音效 |
| voiceovers | 1-5万 | 配音 |
| file_checksums | 14-100万 | 去重表 |
| 总计 | 28-200万 | 分散在6个表中 |
5.3 对比分析
| 方案 | 单表最大数据量 | 查询性能 | 扩展性 |
|---|---|---|---|
| 单一附件表 | 28-200万 | ❌ 差 | ❌ 差 |
| 业务分离方案 | 5-50万 | ✅ 好 | ✅ 好 |
性能优化策略
6.1 索引策略
-- attachments 表
CREATE INDEX idx_attachments_project_id ON attachments (project_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_attachments_script_id ON attachments (script_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_attachments_checksum ON attachments (checksum);
-- project_resources 表
CREATE INDEX idx_project_resources_project_id ON project_resources (project_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_project_resources_type ON project_resources (type) WHERE deleted_at IS NULL;
CREATE INDEX idx_project_resources_checksum ON project_resources (checksum);
-- file_checksums 表
CREATE INDEX idx_file_checksums_reference_count ON file_checksums (reference_count);
CREATE INDEX idx_file_checksums_last_accessed ON file_checksums (last_accessed_at);
6.2 缓存策略
# Redis 缓存热点文件
# Key: file:checksum:{checksum}
# Value: {file_url, file_size, mime_type}
# TTL: 7天
# 缓存项目素材列表
# Key: project:resources:{project_id}:{type}
# Value: [resource_ids]
# TTL: 1小时
6.3 CDN 加速
用户请求文件
↓
CDN 缓存
├─ 命中 → 直接返回
└─ 未命中 → 回源到对象存储
6.4 定时清理
# 每天凌晨3点执行
# 清理30天未访问且无引用的文件
@celery_app.task
async def cleanup_unused_files():
file_storage = FileStorageService(db)
deleted_count = await file_storage.cleanup_unused_files(days=30)
实施计划
7.1 Phase 1: 数据库迁移(如果已有数据)
-- 1. 创建新表
CREATE TABLE project_resources (...);
CREATE TABLE file_checksums (...);
-- 2. 数据迁移
INSERT INTO project_resources (...)
SELECT ... FROM resources WHERE ...;
-- 3. 更新业务表
ALTER TABLE users ADD COLUMN avatar_id BIGINT;
ALTER TABLE projects ADD COLUMN cover_image_id BIGINT;
ALTER TABLE storyboards ADD COLUMN thumbnail_id BIGINT;
-- 4. 数据迁移
UPDATE users SET avatar_id = (SELECT attachment_id FROM attachments WHERE ...);
-- 5. 清理旧数据
-- 备份后删除
7.2 Phase 2: 代码更新
- 创建 FileStorageService
- 更新 AttachmentService
- 创建 ProjectResourceService
- 更新 API 接口
- 更新前端调用
7.3 Phase 3: 测试验证
- 单元测试:各服务功能测试
- 集成测试:文件上传、下载、删除流程测试
- 性能测试:大数据量查询性能测试
- 压力测试:并发上传测试
7.4 Phase 4: 上线部署
- 灰度发布:先在测试环境验证
- 数据备份:上线前完整备份
- 监控告警:监控文件上传、下载成功率
- 回滚方案:准备回滚脚本
总结
优势
✅ 业务边界清晰:附件、素材、视频、音频各司其职
✅ 表大小可控:每个表都在合理范围内(5-50万条)
✅ 查询性能好:无需复杂 JOIN,可针对性优化
✅ 代码复用:FileStorageService 统一处理上传、去重
✅ 去重机制:file_checksums 表全局去重,节省存储
✅ 扩展性好:后期可无缝扩展素材市场功能
风险与应对
| 风险 | 应对措施 |
|---|---|
| 数据迁移失败 | 提前备份,分步迁移,验证数据完整性 |
| API 兼容性问题 | 保持接口向后兼容,提供迁移文档 |
| 性能下降 | 压力测试,优化索引,增加缓存 |
| 文件引用计数错误 | 定期校验,自动修复脚本 |
文档版本:v1.0
创建时间:2025-01-27