mixvideo-v2/.promptx/database-design-standards.md

580 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MixVideo 数据库设计规范
## 数据库技术栈
### 核心技术
- **SQLite 3.40+**: 嵌入式数据库
- **WAL模式**: 提高并发性能
- **外键约束**: 保证数据一致性
- **连接池**: 支持高并发访问
- **读写分离**: 优化查询性能
### 数据库配置
```sql
-- 启用WAL模式
PRAGMA journal_mode = WAL;
-- 启用外键约束
PRAGMA foreign_keys = ON;
-- 设置同步模式
PRAGMA synchronous = NORMAL;
-- 设置缓存大小
PRAGMA cache_size = 10000;
-- 设置超时时间
PRAGMA busy_timeout = 5000;
-- 启用安全删除
PRAGMA secure_delete = ON;
-- 设置临时存储
PRAGMA temp_store = MEMORY;
```
## 表设计规范
### 命名规范
- **表名**: snake_case复数形式 (如 `materials`, `projects`)
- **字段名**: snake_case (如 `created_at`, `project_id`)
- **索引名**: `idx_表名_字段名` (如 `idx_materials_project_id`)
- **外键名**: `fk_表名_引用表名` (如 `fk_materials_projects`)
### 字段设计规范
```sql
-- 主键设计
id TEXT PRIMARY KEY NOT NULL, -- 使用UUID字符串
-- 时间戳字段
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
-- 软删除字段
is_active INTEGER NOT NULL DEFAULT 1,
-- 外键字段
project_id TEXT NOT NULL,
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
-- 枚举字段使用TEXT存储JSON
material_type TEXT NOT NULL CHECK (material_type IN ('video', 'audio', 'image')),
-- 可选字段
description TEXT,
duration INTEGER, -- 可为NULL
-- 文件路径字段
path TEXT NOT NULL,
size INTEGER NOT NULL CHECK (size > 0),
```
## 核心表结构
### 1. 项目表 (projects)
```sql
CREATE TABLE projects (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT,
path TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1
);
-- 索引
CREATE INDEX idx_projects_name ON projects(name);
CREATE INDEX idx_projects_path ON projects(path);
CREATE INDEX idx_projects_created_at ON projects(created_at);
```
### 2. 素材表 (materials)
```sql
CREATE TABLE materials (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
path TEXT NOT NULL,
material_type TEXT NOT NULL CHECK (material_type IN ('video', 'audio', 'image')),
size INTEGER NOT NULL CHECK (size > 0),
duration INTEGER, -- 视频/音频时长(秒)
width INTEGER, -- 图片/视频宽度
height INTEGER, -- 图片/视频高度
fps REAL, -- 视频帧率
project_id TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
);
-- 索引
CREATE INDEX idx_materials_project_id ON materials(project_id);
CREATE INDEX idx_materials_type ON materials(material_type);
CREATE INDEX idx_materials_name ON materials(name);
CREATE INDEX idx_materials_created_at ON materials(created_at);
CREATE UNIQUE INDEX idx_materials_path_project ON materials(path, project_id);
```
### 3. 模特表 (models)
```sql
CREATE TABLE models (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
stage_name TEXT,
gender TEXT NOT NULL CHECK (gender IN ('male', 'female', 'other')),
age INTEGER CHECK (age > 0 AND age < 150),
height INTEGER CHECK (height > 0 AND height < 300), -- 厘米
weight INTEGER CHECK (weight > 0 AND weight < 500), -- 公斤
measurements TEXT, -- JSON格式存储三围
description TEXT,
tags TEXT, -- JSON数组格式存储标签
avatar_path TEXT,
contact_info TEXT, -- JSON格式存储联系信息
social_media TEXT, -- JSON格式存储社交媒体
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'archived')),
rating REAL CHECK (rating >= 1.0 AND rating <= 5.0),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1
);
-- 索引
CREATE INDEX idx_models_name ON models(name);
CREATE INDEX idx_models_gender ON models(gender);
CREATE INDEX idx_models_status ON models(status);
CREATE INDEX idx_models_rating ON models(rating);
```
### 4. 模特照片表 (model_photos)
```sql
CREATE TABLE model_photos (
id TEXT PRIMARY KEY NOT NULL,
model_id TEXT NOT NULL,
photo_type TEXT NOT NULL CHECK (photo_type IN ('portrait', 'full_body', 'casual', 'formal', 'swimwear', 'other')),
path TEXT NOT NULL,
description TEXT,
is_primary INTEGER NOT NULL DEFAULT 0, -- 是否为主要照片
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (model_id) REFERENCES models(id) ON DELETE CASCADE
);
-- 索引
CREATE INDEX idx_model_photos_model_id ON model_photos(model_id);
CREATE INDEX idx_model_photos_type ON model_photos(photo_type);
CREATE INDEX idx_model_photos_primary ON model_photos(is_primary);
CREATE UNIQUE INDEX idx_model_photos_path ON model_photos(path);
```
### 5. 模板表 (templates)
```sql
CREATE TABLE templates (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT,
canvas_config TEXT NOT NULL, -- JSON格式存储画布配置
duration INTEGER NOT NULL, -- 模板总时长(微秒)
fps REAL NOT NULL,
materials TEXT, -- JSON格式存储模板素材
tracks TEXT, -- JSON格式存储轨道信息
import_status TEXT NOT NULL DEFAULT 'pending' CHECK (import_status IN ('pending', 'processing', 'completed', 'failed')),
source_file_path TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1
);
-- 索引
CREATE INDEX idx_templates_name ON templates(name);
CREATE INDEX idx_templates_status ON templates(import_status);
CREATE INDEX idx_templates_created_at ON templates(created_at);
```
### 6. AI分类记录表 (video_classifications)
```sql
CREATE TABLE video_classifications (
id TEXT PRIMARY KEY NOT NULL,
material_id TEXT NOT NULL,
classification_result TEXT NOT NULL, -- JSON格式存储分类结果
confidence_score REAL,
processing_time INTEGER, -- 处理时间(毫秒)
ai_model_version TEXT,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'failed')),
error_message TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE CASCADE
);
-- 索引
CREATE INDEX idx_video_classifications_material_id ON video_classifications(material_id);
CREATE INDEX idx_video_classifications_status ON video_classifications(status);
CREATE INDEX idx_video_classifications_created_at ON video_classifications(created_at);
```
### 7. 穿搭图片表 (outfit_images)
```sql
CREATE TABLE outfit_images (
id TEXT PRIMARY KEY NOT NULL,
model_id TEXT NOT NULL,
image_path TEXT NOT NULL,
prompt TEXT,
negative_prompt TEXT,
generation_params TEXT, -- JSON格式存储生成参数
is_favorite INTEGER NOT NULL DEFAULT 0,
tags TEXT, -- JSON数组格式存储标签
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (model_id) REFERENCES models(id) ON DELETE CASCADE
);
-- 索引
CREATE INDEX idx_outfit_images_model_id ON outfit_images(model_id);
CREATE INDEX idx_outfit_images_favorite ON outfit_images(is_favorite);
CREATE INDEX idx_outfit_images_created_at ON outfit_images(created_at);
CREATE UNIQUE INDEX idx_outfit_images_path ON outfit_images(image_path);
```
### 8. 对话记录表 (conversations)
```sql
CREATE TABLE conversations (
id TEXT PRIMARY KEY NOT NULL,
session_id TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
content TEXT NOT NULL,
metadata TEXT, -- JSON格式存储元数据
created_at TEXT NOT NULL DEFAULT (datetime('now')),
is_active INTEGER NOT NULL DEFAULT 1
);
-- 索引
CREATE INDEX idx_conversations_session_id ON conversations(session_id);
CREATE INDEX idx_conversations_role ON conversations(role);
CREATE INDEX idx_conversations_created_at ON conversations(created_at);
```
## 数据库迁移规范
### 迁移文件结构
```
migrations/
├── 001_initial_schema.sql
├── 002_add_model_photos.sql
├── 003_add_video_classifications.sql
├── 004_add_outfit_images.sql
└── 005_add_conversations.sql
```
### 迁移脚本模板
```sql
-- Migration: 001_initial_schema.sql
-- Description: 创建初始数据库结构
-- Date: 2024-01-01
BEGIN TRANSACTION;
-- 创建项目表
CREATE TABLE projects (
-- 表结构定义
);
-- 创建索引
CREATE INDEX idx_projects_name ON projects(name);
-- 插入初始数据(如果需要)
INSERT INTO projects (id, name, path) VALUES
('default', 'Default Project', '/default/path');
COMMIT;
```
### 迁移管理
```rust
pub struct DatabaseMigration {
version: u32,
description: String,
sql: String,
}
impl Database {
pub async fn migrate(&self) -> Result<()> {
// 创建迁移记录表
self.create_migration_table().await?;
// 获取当前版本
let current_version = self.get_current_version().await?;
// 执行待处理的迁移
let migrations = self.get_pending_migrations(current_version)?;
for migration in migrations {
self.execute_migration(&migration).await?;
}
Ok(())
}
}
```
## 查询优化规范
### 索引策略
```sql
-- 单列索引
CREATE INDEX idx_materials_project_id ON materials(project_id);
-- 复合索引
CREATE INDEX idx_materials_project_type ON materials(project_id, material_type);
-- 唯一索引
CREATE UNIQUE INDEX idx_projects_path ON projects(path);
-- 部分索引
CREATE INDEX idx_active_materials ON materials(project_id) WHERE is_active = 1;
```
### 查询优化
```sql
-- ✅ 使用索引的查询
SELECT * FROM materials
WHERE project_id = ? AND material_type = 'video'
ORDER BY created_at DESC
LIMIT 20;
-- ✅ 使用EXISTS而不是IN
SELECT * FROM projects p
WHERE EXISTS (
SELECT 1 FROM materials m
WHERE m.project_id = p.id AND m.is_active = 1
);
-- ✅ 使用适当的JOIN
SELECT p.name, COUNT(m.id) as material_count
FROM projects p
LEFT JOIN materials m ON p.id = m.project_id AND m.is_active = 1
WHERE p.is_active = 1
GROUP BY p.id, p.name;
```
### 分页查询
```sql
-- 基于OFFSET的分页适用于小数据集
SELECT * FROM materials
WHERE project_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?;
-- 基于游标的分页(适用于大数据集)
SELECT * FROM materials
WHERE project_id = ? AND created_at < ?
ORDER BY created_at DESC
LIMIT ?;
```
## 数据完整性规范
### 外键约束
```sql
-- 级联删除
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
-- 限制删除
FOREIGN KEY (model_id) REFERENCES models(id) ON DELETE RESTRICT
-- 设置NULL
FOREIGN KEY (parent_id) REFERENCES categories(id) ON DELETE SET NULL
```
### 检查约束
```sql
-- 数值范围检查
CHECK (age > 0 AND age < 150)
-- 枚举值检查
CHECK (status IN ('active', 'inactive', 'archived'))
-- 字符串长度检查
CHECK (length(name) > 0 AND length(name) <= 255)
```
### 触发器
```sql
-- 自动更新时间戳
CREATE TRIGGER update_materials_timestamp
AFTER UPDATE ON materials
FOR EACH ROW
BEGIN
UPDATE materials SET updated_at = datetime('now') WHERE id = NEW.id;
END;
-- 软删除触发器
CREATE TRIGGER soft_delete_materials
INSTEAD OF DELETE ON materials
FOR EACH ROW
BEGIN
UPDATE materials SET is_active = 0, updated_at = datetime('now') WHERE id = OLD.id;
END;
```
## 备份和恢复规范
### 备份策略
```rust
pub struct DatabaseBackup {
source_path: PathBuf,
backup_dir: PathBuf,
}
impl DatabaseBackup {
pub async fn create_backup(&self) -> Result<PathBuf> {
let timestamp = Utc::now().format("%Y%m%d_%H%M%S");
let backup_name = format!("mixvideo_backup_{}.db", timestamp);
let backup_path = self.backup_dir.join(backup_name);
// 使用SQLite的备份API
let source = Connection::open(&self.source_path)?;
let backup = Connection::open(&backup_path)?;
let backup_handle = rusqlite::backup::Backup::new(&source, &backup)?;
backup_handle.run_to_completion(5, Duration::from_millis(250), None)?;
Ok(backup_path)
}
pub async fn restore_backup(&self, backup_path: &Path) -> Result<()> {
// 验证备份文件
self.validate_backup(backup_path)?;
// 创建当前数据库的备份
let current_backup = self.create_backup().await?;
// 恢复数据库
std::fs::copy(backup_path, &self.source_path)?;
// 验证恢复的数据库
self.validate_database().await?;
Ok(())
}
}
```
### 数据验证
```sql
-- 检查数据完整性
PRAGMA integrity_check;
-- 检查外键约束
PRAGMA foreign_key_check;
-- 分析数据库统计信息
ANALYZE;
```
## 性能监控规范
### 查询性能分析
```sql
-- 启用查询计划分析
EXPLAIN QUERY PLAN SELECT * FROM materials WHERE project_id = ?;
-- 查看表统计信息
SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index';
-- 检查表大小
SELECT
name,
COUNT(*) as row_count,
SUM(length(sql)) as size_estimate
FROM sqlite_master
WHERE type = 'table'
GROUP BY name;
```
### 性能监控
```rust
pub struct DatabaseMetrics {
query_times: HashMap<String, Vec<Duration>>,
connection_pool_stats: ConnectionPoolStats,
}
impl DatabaseMetrics {
pub fn record_query_time(&mut self, query: &str, duration: Duration) {
self.query_times
.entry(query.to_string())
.or_insert_with(Vec::new)
.push(duration);
}
pub fn get_slow_queries(&self, threshold: Duration) -> Vec<(String, Duration)> {
self.query_times
.iter()
.filter_map(|(query, times)| {
let avg_time = times.iter().sum::<Duration>() / times.len() as u32;
if avg_time > threshold {
Some((query.clone(), avg_time))
} else {
None
}
})
.collect()
}
}
```
## 安全规范
### 数据加密
```rust
// 敏感数据加密存储
pub fn encrypt_sensitive_data(data: &str, key: &[u8]) -> Result<String> {
// 使用AES加密
let cipher = Aes256Gcm::new(key.into());
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, data.as_bytes())?;
// 组合nonce和密文
let mut result = nonce.to_vec();
result.extend_from_slice(&ciphertext);
Ok(base64::encode(result))
}
```
### 访问控制
```sql
-- 创建只读视图
CREATE VIEW materials_readonly AS
SELECT id, name, material_type, size, created_at
FROM materials
WHERE is_active = 1;
-- 限制敏感字段访问
CREATE VIEW models_public AS
SELECT id, name, stage_name, gender, description, tags, avatar_path, rating
FROM models
WHERE is_active = 1;
```
### 审计日志
```sql
CREATE TABLE audit_logs (
id TEXT PRIMARY KEY NOT NULL,
table_name TEXT NOT NULL,
operation TEXT NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE')),
record_id TEXT NOT NULL,
old_values TEXT, -- JSON格式
new_values TEXT, -- JSON格式
user_id TEXT,
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
);
```