fix: 模板移除project关联

This commit is contained in:
imeepos
2025-07-15 08:42:36 +08:00
parent 595d2f75fd
commit 14d90b2254
5 changed files with 90 additions and 58 deletions

View File

@@ -201,9 +201,11 @@ impl DraftContentParser {
material.template_id = template.id.clone(); material.template_id = template.id.clone();
} }
// 构建素材映射表素材ID -> original_id (用于片段关联)
// 片段的 material_id 对应素材的 id需要映射到 original_id
let material_map: HashMap<String, String> = materials let material_map: HashMap<String, String> = materials
.iter() .iter()
.map(|m| (m.original_id.clone(), m.id.clone())) .map(|m| (m.id.clone(), m.original_id.clone())) // 键是素材ID值是original_id
.collect(); .collect();
for material in materials { for material in materials {
@@ -330,21 +332,20 @@ impl DraftContentParser {
missing_files.push(video.path.clone()); missing_files.push(video.path.clone());
} }
// original_id 使用 local_material_id如果没有则使用空字符串
let original_id = video.local_material_id.clone()
.filter(|id| !id.is_empty())
.unwrap_or_default();
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
video.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
video.id.clone(), // 主键使用 id original_id, // original_id 对应 local_material_id
name, name,
TemplateMaterialType::Video, TemplateMaterialType::Video,
video.path.clone(), video.path.clone(),
); );
// 设置 original_id 为 local_material_id
if let Some(local_material_id) = &video.local_material_id {
if !local_material_id.is_empty() {
material.original_id = local_material_id.clone();
}
}
material.duration = video.duration; material.duration = video.duration;
material.width = video.width; material.width = video.width;
material.height = video.height; material.height = video.height;
@@ -367,14 +368,15 @@ impl DraftContentParser {
missing_files.push(audio.path.clone()); missing_files.push(audio.path.clone());
} }
// 使用 local_material_id 作为主键,如果没有则使用 id // original_id 使用 local_material_id如果没有则使用空字符串
let material_id = audio.local_material_id.clone() let original_id = audio.local_material_id.clone()
.filter(|id| !id.is_empty()) .filter(|id| !id.is_empty())
.unwrap_or_else(|| audio.id.clone()); .unwrap_or_default();
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
audio.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
material_id, // 使用 local_material_id 作为主键 original_id, // original_id 对应 local_material_id
audio.name.clone(), audio.name.clone(),
TemplateMaterialType::Audio, TemplateMaterialType::Audio,
audio.path.clone(), audio.path.clone(),
@@ -413,9 +415,15 @@ impl DraftContentParser {
missing_files.push(path.clone()); missing_files.push(path.clone());
} }
// original_id 使用 local_material_id如果没有则使用空字符串
let original_id = image.local_material_id.clone()
.filter(|id| !id.is_empty())
.unwrap_or_default();
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
image.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
image.id.clone(), original_id, // original_id 对应 local_material_id
name, name,
TemplateMaterialType::Image, TemplateMaterialType::Image,
path, path,
@@ -439,8 +447,9 @@ impl DraftContentParser {
let name = text.content.clone().unwrap_or_else(|| "文本".to_string()); let name = text.content.clone().unwrap_or_else(|| "文本".to_string());
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
text.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
text.id.clone(), String::new(), // 文字素材没有 local_material_id
name, name,
TemplateMaterialType::Text, TemplateMaterialType::Text,
String::new(), // 文本素材没有文件路径 String::new(), // 文本素材没有文件路径
@@ -472,8 +481,9 @@ impl DraftContentParser {
} }
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
sticker.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
sticker.id.clone(), String::new(), // 贴纸素材没有 local_material_id
name, name,
TemplateMaterialType::Sticker, TemplateMaterialType::Sticker,
path, path,
@@ -503,8 +513,9 @@ impl DraftContentParser {
} }
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
effect.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
effect.id.clone(), String::new(), // 特效素材没有 local_material_id
name, name,
TemplateMaterialType::Effect, TemplateMaterialType::Effect,
path, path,
@@ -525,8 +536,9 @@ impl DraftContentParser {
let name = "画布".to_string(); let name = "画布".to_string();
let mut material = TemplateMaterial::new( let mut material = TemplateMaterial::new(
canvas.id.clone(), // 使用素材的真实ID
String::new(), // template_id 稍后设置 String::new(), // template_id 稍后设置
canvas.id.clone(), String::new(), // 画布素材没有 local_material_id
name, name,
TemplateMaterialType::Canvas, TemplateMaterialType::Canvas,
String::new(), // 画布素材没有文件路径 String::new(), // 画布素材没有文件路径
@@ -562,6 +574,7 @@ impl DraftContentParser {
let track_name = format!("轨道{} ({})", track_index + 1, track_raw.track_type); let track_name = format!("轨道{} ({})", track_index + 1, track_raw.track_type);
let mut track = Track::new( let mut track = Track::new(
track_raw.id.clone(), // 使用轨道的真实ID
template_id.to_string(), template_id.to_string(),
track_name, track_name,
track_type, track_type,
@@ -603,6 +616,7 @@ impl DraftContentParser {
let segment_name = format!("片段{}", segment_index + 1); let segment_name = format!("片段{}", segment_index + 1);
let mut segment = TrackSegment::new( let mut segment = TrackSegment::new(
segment_raw.id.clone(), // 使用片段的真实ID
track_id.to_string(), track_id.to_string(),
segment_name, segment_name,
start_time, start_time,

View File

@@ -187,7 +187,6 @@ impl TemplateImportService {
info!( info!(
template_id = %template.id, template_id = %template.id,
template_name = %template.name, template_name = %template.name,
project_id = ?template.project_id,
"开始保存模板到数据库" "开始保存模板到数据库"
); );
template.update_import_status(ImportStatus::Processing); template.update_import_status(ImportStatus::Processing);
@@ -311,12 +310,8 @@ impl TemplateImportService {
return Err(anyhow!("模板验证失败: {}", validation_errors.join(", "))); return Err(anyhow!("模板验证失败: {}", validation_errors.join(", ")));
} }
// 设置项目关联
let mut template = parse_result.template.clone();
template.project_id = request.project_id.clone();
Ok(ParseResult { Ok(ParseResult {
template, template: parse_result.template,
missing_files: parse_result.missing_files, missing_files: parse_result.missing_files,
warnings: parse_result.warnings, warnings: parse_result.warnings,
}) })

View File

@@ -71,19 +71,14 @@ impl TemplateService {
// 保存模板基本信息 // 保存模板基本信息
tx.execute( tx.execute(
"INSERT OR REPLACE INTO templates ( "INSERT OR REPLACE INTO templates (
id, name, description, project_id, canvas_width, canvas_height, id, name, description, canvas_width, canvas_height,
canvas_ratio, duration, fps, import_status, source_file_path, canvas_ratio, duration, fps, import_status, source_file_path,
created_at, updated_at, is_active created_at, updated_at, is_active
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)", ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)",
params![ params![
template.id, template.id,
template.name, template.name,
template.description, template.description,
if template.project_id.as_ref().map_or(true, |id| id.is_empty()) {
None::<String>
} else {
template.project_id.clone()
},
template.canvas_config.width, template.canvas_config.width,
template.canvas_config.height, template.canvas_config.height,
template.canvas_config.ratio, template.canvas_config.ratio,
@@ -183,7 +178,7 @@ impl TemplateService {
// 查询模板基本信息 // 查询模板基本信息
let template_result = conn.query_row( let template_result = conn.query_row(
"SELECT id, name, description, project_id, canvas_width, canvas_height, "SELECT id, name, description, canvas_width, canvas_height,
canvas_ratio, duration, fps, import_status, source_file_path, canvas_ratio, duration, fps, import_status, source_file_path,
created_at, updated_at, is_active created_at, updated_at, is_active
FROM templates WHERE id = ?1", FROM templates WHERE id = ?1",
@@ -333,7 +328,7 @@ impl TemplateService {
|row| Ok(row.get::<_, i64>(0)? as u32) |row| Ok(row.get::<_, i64>(0)? as u32)
)?; )?;
let mut sql = "SELECT id, name, description, project_id, canvas_width, canvas_height, let mut sql = "SELECT id, name, description, canvas_width, canvas_height,
canvas_ratio, duration, fps, import_status, source_file_path, canvas_ratio, duration, fps, import_status, source_file_path,
created_at, updated_at, is_active created_at, updated_at, is_active
FROM templates WHERE is_active = 1 ORDER BY created_at DESC".to_string(); FROM templates WHERE is_active = 1 ORDER BY created_at DESC".to_string();
@@ -402,7 +397,6 @@ impl TemplateService {
id: row.get("id")?, id: row.get("id")?,
name: row.get("name")?, name: row.get("name")?,
description: row.get("description")?, description: row.get("description")?,
project_id: row.get("project_id")?,
canvas_config, canvas_config,
duration: row.get::<_, i64>("duration")? as u64, duration: row.get::<_, i64>("duration")? as u64,
fps: row.get("fps")?, fps: row.get("fps")?,

View File

@@ -8,7 +8,6 @@ pub struct Template {
pub id: String, pub id: String,
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub project_id: Option<String>, // 关联的项目ID
pub canvas_config: CanvasConfig, pub canvas_config: CanvasConfig,
pub duration: u64, // 模板总时长(微秒) pub duration: u64, // 模板总时长(微秒)
pub fps: f64, pub fps: f64,
@@ -179,7 +178,6 @@ impl Template {
id: uuid::Uuid::new_v4().to_string(), id: uuid::Uuid::new_v4().to_string(),
name, name,
description: None, description: None,
project_id: None,
canvas_config, canvas_config,
duration, duration,
fps, fps,
@@ -237,17 +235,18 @@ impl Template {
impl TemplateMaterial { impl TemplateMaterial {
/// 创建新的模板素材实例 /// 创建新的模板素材实例
pub fn new( pub fn new(
id: String, // 素材的真实ID来自剪映草稿
template_id: String, template_id: String,
original_id: String, original_id: String, // local_material_id
name: String, name: String,
material_type: TemplateMaterialType, material_type: TemplateMaterialType,
original_path: String, original_path: String,
) -> Self { ) -> Self {
let now = Utc::now(); let now = Utc::now();
Self { Self {
id: original_id.clone(), // 使用原始ID作为主键保持与剪映草稿的一致性 id, // 使用传入的真实ID
template_id, template_id,
original_id, original_id, // original_id 对应 local_material_id可以重复
name, name,
material_type, material_type,
original_path, original_path,
@@ -298,6 +297,7 @@ impl TemplateMaterial {
impl Track { impl Track {
/// 创建新的轨道实例 /// 创建新的轨道实例
pub fn new( pub fn new(
id: String, // 轨道的真实ID来自剪映草稿
template_id: String, template_id: String,
name: String, name: String,
track_type: TrackType, track_type: TrackType,
@@ -305,7 +305,7 @@ impl Track {
) -> Self { ) -> Self {
let now = Utc::now(); let now = Utc::now();
Self { Self {
id: uuid::Uuid::new_v4().to_string(), id, // 使用传入的真实ID
template_id, template_id,
name, name,
track_type, track_type,
@@ -335,6 +335,7 @@ impl Track {
impl TrackSegment { impl TrackSegment {
/// 创建新的轨道片段实例 /// 创建新的轨道片段实例
pub fn new( pub fn new(
id: String, // 片段的真实ID来自剪映草稿
track_id: String, track_id: String,
name: String, name: String,
start_time: u64, start_time: u64,
@@ -343,7 +344,7 @@ impl TrackSegment {
) -> Self { ) -> Self {
let now = Utc::now(); let now = Utc::now();
Self { Self {
id: uuid::Uuid::new_v4().to_string(), id, // 使用传入的真实ID
track_id, track_id,
template_material_id: None, template_material_id: None,
name, name,

View File

@@ -265,7 +265,6 @@ impl Database {
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
description TEXT, description TEXT,
project_id TEXT,
canvas_width INTEGER NOT NULL, canvas_width INTEGER NOT NULL,
canvas_height INTEGER NOT NULL, canvas_height INTEGER NOT NULL,
canvas_ratio TEXT NOT NULL, canvas_ratio TEXT NOT NULL,
@@ -275,8 +274,7 @@ impl Database {
source_file_path TEXT, source_file_path TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1, is_active BOOLEAN DEFAULT 1
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE SET NULL
)", )",
[], [],
)?; )?;
@@ -380,11 +378,6 @@ impl Database {
)?; )?;
// 创建模板表索引 // 创建模板表索引
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_templates_project_id ON templates (project_id)",
[],
)?;
conn.execute( conn.execute(
"CREATE INDEX IF NOT EXISTS idx_templates_import_status ON templates (import_status)", "CREATE INDEX IF NOT EXISTS idx_templates_import_status ON templates (import_status)",
[], [],
@@ -406,6 +399,41 @@ impl Database {
[], [],
); );
// 移除模板表的 project_id 字段(如果存在)
// SQLite 不支持 DROP COLUMN需要重建表
let _ = conn.execute(
"CREATE TABLE IF NOT EXISTS templates_new (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
canvas_width INTEGER NOT NULL,
canvas_height INTEGER NOT NULL,
canvas_ratio TEXT NOT NULL,
duration INTEGER NOT NULL,
fps REAL NOT NULL,
import_status TEXT NOT NULL DEFAULT 'Pending',
source_file_path TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1
)",
[],
);
// 迁移数据(排除 project_id
let _ = conn.execute(
"INSERT OR IGNORE INTO templates_new
SELECT id, name, description, canvas_width, canvas_height, canvas_ratio,
duration, fps, import_status, source_file_path, created_at, updated_at, is_active
FROM templates",
[],
);
// 删除旧表并重命名新表
let _ = conn.execute("DROP TABLE IF EXISTS templates_old", []);
let _ = conn.execute("ALTER TABLE templates RENAME TO templates_old", []);
let _ = conn.execute("ALTER TABLE templates_new RENAME TO templates", []);
// 创建模板素材表索引 // 创建模板素材表索引
conn.execute( conn.execute(
"CREATE INDEX IF NOT EXISTS idx_template_materials_template_id ON template_materials (template_id)", "CREATE INDEX IF NOT EXISTS idx_template_materials_template_id ON template_materials (template_id)",