fix: 修复数据库迁移问题
- 将表创建和索引创建逻辑从仓储初始化方法移到迁移系统 - 移除 outfit_image_repository.rs 中的 init_tables 表创建逻辑 - 确保所有数据库结构变更通过迁移系统统一管理 - 修复 comfyui_prompt_id 索引创建时机问题 这样可以避免在旧数据库上创建索引时找不到字段的错误。
This commit is contained in:
parent
4b20f69560
commit
676effdab4
|
|
@ -82,13 +82,11 @@ impl ModelService {
|
||||||
pub fn get_all_models(
|
pub fn get_all_models(
|
||||||
repository: &ModelRepository,
|
repository: &ModelRepository,
|
||||||
) -> Result<Vec<Model>> {
|
) -> Result<Vec<Model>> {
|
||||||
println!("ModelService::get_all_models 开始执行");
|
|
||||||
let result = repository.get_all()
|
let result = repository.get_all()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
println!("repository.get_all() 失败: {}", e);
|
println!("repository.get_all() 失败: {}", e);
|
||||||
anyhow!("获取模特列表失败: {}", e)
|
anyhow!("获取模特列表失败: {}", e)
|
||||||
});
|
});
|
||||||
println!("ModelService::get_all_models 执行完成,结果: {:?}", result.is_ok());
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,6 @@ impl ModelRepository {
|
||||||
|
|
||||||
/// 获取所有模特
|
/// 获取所有模特
|
||||||
pub fn get_all(&self) -> Result<Vec<Model>> {
|
pub fn get_all(&self) -> Result<Vec<Model>> {
|
||||||
println!("ModelRepository::get_all 开始执行");
|
|
||||||
|
|
||||||
// 首先获取所有模特的基本信息
|
// 首先获取所有模特的基本信息
|
||||||
let mut models = {
|
let mut models = {
|
||||||
|
|
@ -202,11 +201,8 @@ impl ModelRepository {
|
||||||
|
|
||||||
let mut models = Vec::new();
|
let mut models = Vec::new();
|
||||||
for (index, model) in model_iter.enumerate() {
|
for (index, model) in model_iter.enumerate() {
|
||||||
println!("处理第 {} 个模特记录", index + 1);
|
|
||||||
println!("开始解析模特数据...");
|
|
||||||
let model = match model {
|
let model = match model {
|
||||||
Ok(m) => {
|
Ok(m) => {
|
||||||
println!("模特数据解析成功: {}", m.name);
|
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -215,17 +211,14 @@ impl ModelRepository {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
models.push(model);
|
models.push(model);
|
||||||
println!("第 {} 个模特记录处理完成", index + 1);
|
|
||||||
}
|
}
|
||||||
models
|
models
|
||||||
}; // 释放数据库连接锁
|
}; // 释放数据库连接锁
|
||||||
|
|
||||||
// 然后为每个模特加载照片信息(避免嵌套锁)
|
// 然后为每个模特加载照片信息(避免嵌套锁)
|
||||||
for model in &mut models {
|
for model in &mut models {
|
||||||
println!("开始加载模特 {} 的照片信息", model.id);
|
|
||||||
model.photos = match self.get_photos(&model.id) {
|
model.photos = match self.get_photos(&model.id) {
|
||||||
Ok(photos) => {
|
Ok(photos) => {
|
||||||
println!("照片信息加载成功,共 {} 张照片", photos.len());
|
|
||||||
photos
|
photos
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -235,11 +228,6 @@ impl ModelRepository {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
|
||||||
"ModelRepository::get_all 执行完成,返回 {} 个模特",
|
|
||||||
models.len()
|
|
||||||
);
|
|
||||||
Ok(models)
|
Ok(models)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -517,7 +505,6 @@ impl ModelRepository {
|
||||||
description, tags, is_cover, created_at
|
description, tags, is_cover, created_at
|
||||||
FROM model_photos WHERE model_id = ?1 ORDER BY created_at DESC",
|
FROM model_photos WHERE model_id = ?1 ORDER BY created_at DESC",
|
||||||
)?;
|
)?;
|
||||||
println!("get_photos SQL 语句准备成功");
|
|
||||||
|
|
||||||
let photo_iter = stmt.query_map([model_id], |row| self.row_to_photo(row))?;
|
let photo_iter = stmt.query_map([model_id], |row| self.row_to_photo(row))?;
|
||||||
|
|
||||||
|
|
@ -732,7 +719,6 @@ impl ModelRepository {
|
||||||
println!("row_to_photo photo_type 解析失败: {}", e);
|
println!("row_to_photo photo_type 解析失败: {}", e);
|
||||||
rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(e))
|
rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(e))
|
||||||
})?;
|
})?;
|
||||||
println!("row_to_photo photo_type 解析成功");
|
|
||||||
|
|
||||||
let tags_str: String = row.get("tags")?;
|
let tags_str: String = row.get("tags")?;
|
||||||
let tags: Vec<String> = serde_json::from_str(&tags_str).map_err(|e| {
|
let tags: Vec<String> = serde_json::from_str(&tags_str).map_err(|e| {
|
||||||
|
|
|
||||||
|
|
@ -23,103 +23,8 @@ impl OutfitImageRepository {
|
||||||
|
|
||||||
/// 初始化数据库表(强制使用连接池)
|
/// 初始化数据库表(强制使用连接池)
|
||||||
pub fn init_tables(&self) -> Result<()> {
|
pub fn init_tables(&self) -> Result<()> {
|
||||||
|
// 表创建和索引创建现在通过迁移系统处理
|
||||||
// 🚨 强制使用连接池,避免死锁
|
// 这个方法保留用于未来可能的初始化逻辑
|
||||||
if !self.database.has_pool() {
|
|
||||||
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let pooled_conn = self.database.acquire_from_pool()
|
|
||||||
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
|
|
||||||
|
|
||||||
// 创建穿搭图片生成记录表
|
|
||||||
pooled_conn.execute(
|
|
||||||
r#"
|
|
||||||
CREATE TABLE IF NOT EXISTS outfit_image_records (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
model_id TEXT NOT NULL,
|
|
||||||
model_image_id TEXT NOT NULL,
|
|
||||||
generation_prompt TEXT,
|
|
||||||
status TEXT NOT NULL DEFAULT 'pending',
|
|
||||||
progress REAL NOT NULL DEFAULT 0.0,
|
|
||||||
result_urls TEXT NOT NULL DEFAULT '[]',
|
|
||||||
error_message TEXT,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
started_at DATETIME,
|
|
||||||
completed_at DATETIME,
|
|
||||||
duration_ms INTEGER,
|
|
||||||
comfyui_prompt_id TEXT,
|
|
||||||
FOREIGN KEY (model_id) REFERENCES models (id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (model_image_id) REFERENCES model_photos (id) ON DELETE CASCADE
|
|
||||||
)
|
|
||||||
"#,
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建穿搭图片记录表失败: {}", e))?;
|
|
||||||
|
|
||||||
// 创建商品图片表
|
|
||||||
pooled_conn.execute(
|
|
||||||
r#"
|
|
||||||
CREATE TABLE IF NOT EXISTS product_images (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
outfit_record_id TEXT NOT NULL,
|
|
||||||
file_path TEXT NOT NULL,
|
|
||||||
file_name TEXT NOT NULL,
|
|
||||||
file_size INTEGER NOT NULL,
|
|
||||||
upload_url TEXT,
|
|
||||||
description TEXT,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (outfit_record_id) REFERENCES outfit_image_records (id) ON DELETE CASCADE
|
|
||||||
)
|
|
||||||
"#,
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建商品图片表失败: {}", e))?;
|
|
||||||
|
|
||||||
// 创建穿搭图片表
|
|
||||||
pooled_conn.execute(
|
|
||||||
r#"
|
|
||||||
CREATE TABLE IF NOT EXISTS outfit_images (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
outfit_record_id TEXT NOT NULL,
|
|
||||||
image_url TEXT NOT NULL,
|
|
||||||
local_path TEXT,
|
|
||||||
image_index INTEGER NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
tags TEXT,
|
|
||||||
is_favorite BOOLEAN DEFAULT 0,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (outfit_record_id) REFERENCES outfit_image_records (id) ON DELETE CASCADE
|
|
||||||
)
|
|
||||||
"#,
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建穿搭图片表失败: {}", e))?;
|
|
||||||
|
|
||||||
// 创建索引
|
|
||||||
pooled_conn.execute(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_outfit_image_records_model_id ON outfit_image_records (model_id)",
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建索引失败: {}", e))?;
|
|
||||||
|
|
||||||
|
|
||||||
pooled_conn.execute(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_outfit_image_records_status ON outfit_image_records (status)",
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建索引失败: {}", e))?;
|
|
||||||
|
|
||||||
pooled_conn.execute(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_outfit_image_records_comfyui_prompt_id ON outfit_image_records (comfyui_prompt_id)",
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建索引失败: {}", e))?;
|
|
||||||
|
|
||||||
pooled_conn.execute(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_product_images_outfit_record_id ON product_images (outfit_record_id)",
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建索引失败: {}", e))?;
|
|
||||||
|
|
||||||
pooled_conn.execute(
|
|
||||||
"CREATE INDEX IF NOT EXISTS idx_outfit_images_outfit_record_id ON outfit_images (outfit_record_id)",
|
|
||||||
[],
|
|
||||||
).map_err(|e| anyhow!("创建索引失败: {}", e))?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,6 @@ impl Database {
|
||||||
// 首先尝试专用只读连接
|
// 首先尝试专用只读连接
|
||||||
match self.read_connection.try_lock() {
|
match self.read_connection.try_lock() {
|
||||||
Ok(conn) => {
|
Ok(conn) => {
|
||||||
println!("使用专用只读连接");
|
|
||||||
return Ok(ConnectionHandle::Single(conn));
|
return Ok(ConnectionHandle::Single(conn));
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ pub async fn get_all_models(
|
||||||
"模特仓库未初始化".to_string()
|
"模特仓库未初始化".to_string()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
println!("调用 ModelService::get_all_models...");
|
|
||||||
let result = ModelService::get_all_models(repository)
|
let result = ModelService::get_all_models(repository)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
println!("ModelService::get_all_models 失败: {}", e);
|
println!("ModelService::get_all_models 失败: {}", e);
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ const ModelList: React.FC<ModelListProps> = ({ onModelSelect }) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
console.log('开始加载模特列表...');
|
|
||||||
const modelList = await modelService.getAllModels();
|
const modelList = await modelService.getAllModels();
|
||||||
console.log('模特列表加载成功:', modelList);
|
console.log('模特列表加载成功:', modelList);
|
||||||
setModels(modelList);
|
setModels(modelList);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue