优化 conversation_repository.rs 使用连接池

修复内容:
- initialize_tables 方法强制使用连接池
- 避免 get_connection().lock() 死锁风险
- 添加连接池状态检查和错误处理
- 添加调试日志便于问题排查

这是数据库连接池优化计划的第1步,逐步替换所有旧的单连接模式。
This commit is contained in:
imeepos 2025-07-30 18:51:32 +08:00
parent f31783f682
commit b6999c379b
2 changed files with 92 additions and 36 deletions

View File

@ -22,13 +22,20 @@ impl ConversationRepository {
Self { database } Self { database }
} }
/// 初始化会话相关数据表 /// 初始化会话相关数据表(强制使用连接池)
pub fn initialize_tables(&self) -> Result<()> { pub fn initialize_tables(&self) -> Result<()> {
let conn = self.database.get_connection(); println!("🏊 使用连接池初始化会话表...");
let conn = conn.lock().unwrap();
// 🚨 强制使用连接池,避免死锁
if !self.database.has_pool() {
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
}
let pooled_conn = self.database.acquire_from_pool()
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
// 创建会话表 // 创建会话表
conn.execute( pooled_conn.execute(
"CREATE TABLE IF NOT EXISTS conversation_sessions ( "CREATE TABLE IF NOT EXISTS conversation_sessions (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
title TEXT, title TEXT,
@ -41,7 +48,7 @@ impl ConversationRepository {
)?; )?;
// 创建消息表 // 创建消息表
conn.execute( pooled_conn.execute(
"CREATE TABLE IF NOT EXISTS conversation_messages ( "CREATE TABLE IF NOT EXISTS conversation_messages (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
session_id TEXT NOT NULL, session_id TEXT NOT NULL,
@ -55,18 +62,19 @@ impl ConversationRepository {
)?; )?;
// 创建索引以提高查询性能 // 创建索引以提高查询性能
conn.execute( pooled_conn.execute(
"CREATE INDEX IF NOT EXISTS idx_messages_session_timestamp "CREATE INDEX IF NOT EXISTS idx_messages_session_timestamp
ON conversation_messages (session_id, timestamp)", ON conversation_messages (session_id, timestamp)",
[], [],
)?; )?;
conn.execute( pooled_conn.execute(
"CREATE INDEX IF NOT EXISTS idx_sessions_active_updated "CREATE INDEX IF NOT EXISTS idx_sessions_active_updated
ON conversation_sessions (is_active, updated_at)", ON conversation_sessions (is_active, updated_at)",
[], [],
)?; )?;
println!("✅ 会话表初始化完成");
Ok(()) Ok(())
} }

View File

@ -438,25 +438,40 @@ impl OutfitImageRepository {
Ok(records) Ok(records)
} }
/// 删除穿搭图片生成记录 /// 删除穿搭图片生成记录(强制使用连接池)
pub fn delete_record(&self, id: &str) -> Result<()> { pub fn delete_record(&self, id: &str) -> Result<()> {
let conn = self.database.get_connection(); println!("🏊 使用连接池删除穿搭记录: {}", &id[..8]);
let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?;
conn.execute( // 🚨 强制使用连接池,避免死锁
if !self.database.has_pool() {
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
}
let pooled_conn = self.database.acquire_from_pool()
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
pooled_conn.execute(
"DELETE FROM outfit_image_records WHERE id = ?1", "DELETE FROM outfit_image_records WHERE id = ?1",
params![id], params![id],
).map_err(|e| anyhow!("删除穿搭图片记录失败: {}", e))?; ).map_err(|e| anyhow!("删除穿搭图片记录失败: {}", e))?;
println!("✅ 连接池删除穿搭记录完成");
Ok(()) Ok(())
} }
/// 创建商品图片 /// 创建商品图片(强制使用连接池)
pub fn create_product_image(&self, product_image: &ProductImage) -> Result<()> { pub fn create_product_image(&self, product_image: &ProductImage) -> Result<()> {
let conn = self.database.get_connection(); println!("🏊 使用连接池创建商品图片: {}", &product_image.id[..8]);
let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?;
conn.execute( // 🚨 强制使用连接池,避免死锁
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#" r#"
INSERT INTO product_images ( INSERT INTO product_images (
id, outfit_record_id, file_path, file_name, file_size, id, outfit_record_id, file_path, file_name, file_size,
@ -475,20 +490,28 @@ impl OutfitImageRepository {
], ],
).map_err(|e| anyhow!("创建商品图片失败: {}", e))?; ).map_err(|e| anyhow!("创建商品图片失败: {}", e))?;
println!("✅ 连接池创建商品图片完成");
Ok(()) Ok(())
} }
/// 根据记录ID获取商品图片列表 /// 根据记录ID获取商品图片列表(强制使用连接池)
pub fn get_product_images_by_record_id(&self, record_id: &str) -> Result<Vec<ProductImage>> { pub fn get_product_images_by_record_id(&self, record_id: &str) -> Result<Vec<ProductImage>> {
let conn = self.database.get_connection(); println!("🏊 使用连接池获取商品图片: {}", &record_id[..8]);
let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?;
let mut stmt = conn.prepare( // 🚨 强制使用连接池,避免死锁
if !self.database.has_pool() {
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
}
let pooled_conn = self.database.acquire_from_pool()
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
let mut stmt = pooled_conn.prepare(
r#" r#"
SELECT id, outfit_record_id, file_path, file_name, file_size, SELECT id, outfit_record_id, file_path, file_name, file_size,
upload_url, description, created_at upload_url, description, created_at
FROM product_images FROM product_images
WHERE outfit_record_id = ?1 WHERE outfit_record_id = ?1
ORDER BY created_at ASC ORDER BY created_at ASC
"#, "#,
).map_err(|e| anyhow!("准备查询语句失败: {}", e))?; ).map_err(|e| anyhow!("准备查询语句失败: {}", e))?;
@ -502,18 +525,26 @@ impl OutfitImageRepository {
images.push(image.map_err(|e| anyhow!("解析商品图片失败: {}", e))?); images.push(image.map_err(|e| anyhow!("解析商品图片失败: {}", e))?);
} }
println!("✅ 连接池查询商品图片完成,获取到 {} 张图片", images.len());
Ok(images) Ok(images)
} }
/// 创建穿搭图片 /// 创建穿搭图片(强制使用连接池)
pub fn create_outfit_image(&self, outfit_image: &OutfitImage) -> Result<()> { pub fn create_outfit_image(&self, outfit_image: &OutfitImage) -> Result<()> {
let conn = self.database.get_connection(); println!("🏊 使用连接池创建穿搭图片: {}", &outfit_image.id[..8]);
let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?;
// 🚨 强制使用连接池,避免死锁
if !self.database.has_pool() {
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
}
let pooled_conn = self.database.acquire_from_pool()
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
let tags_json = serde_json::to_string(&outfit_image.tags) let tags_json = serde_json::to_string(&outfit_image.tags)
.map_err(|e| anyhow!("序列化tags失败: {}", e))?; .map_err(|e| anyhow!("序列化tags失败: {}", e))?;
conn.execute( pooled_conn.execute(
r#" r#"
INSERT INTO outfit_images ( INSERT INTO outfit_images (
id, outfit_record_id, image_url, local_path, image_index, id, outfit_record_id, image_url, local_path, image_index,
@ -533,15 +564,23 @@ impl OutfitImageRepository {
], ],
).map_err(|e| anyhow!("创建穿搭图片失败: {}", e))?; ).map_err(|e| anyhow!("创建穿搭图片失败: {}", e))?;
println!("✅ 连接池创建穿搭图片完成");
Ok(()) Ok(())
} }
/// 根据记录ID获取穿搭图片列表 /// 根据记录ID获取穿搭图片列表(强制使用连接池)
pub fn get_outfit_images_by_record_id(&self, record_id: &str) -> Result<Vec<OutfitImage>> { pub fn get_outfit_images_by_record_id(&self, record_id: &str) -> Result<Vec<OutfitImage>> {
let conn = self.database.get_connection(); println!("🏊 使用连接池获取穿搭图片: {}", &record_id[..8]);
let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?;
let mut stmt = conn.prepare( // 🚨 强制使用连接池,避免死锁
if !self.database.has_pool() {
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
}
let pooled_conn = self.database.acquire_from_pool()
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
let mut stmt = pooled_conn.prepare(
r#" r#"
SELECT id, outfit_record_id, image_url, local_path, image_index, SELECT id, outfit_record_id, image_url, local_path, image_index,
description, tags, is_favorite, created_at description, tags, is_favorite, created_at
@ -560,16 +599,24 @@ impl OutfitImageRepository {
images.push(image.map_err(|e| anyhow!("解析穿搭图片失败: {}", e))?); images.push(image.map_err(|e| anyhow!("解析穿搭图片失败: {}", e))?);
} }
println!("✅ 连接池查询穿搭图片完成,获取到 {} 张图片", images.len());
Ok(images) Ok(images)
} }
/// 获取模特的穿搭图片统计信息 /// 获取模特的穿搭图片统计信息(强制使用连接池)
pub fn get_stats_by_model_id(&self, model_id: &str) -> Result<OutfitImageStats> { pub fn get_stats_by_model_id(&self, model_id: &str) -> Result<OutfitImageStats> {
let conn = self.database.get_connection(); println!("🏊 使用连接池获取穿搭统计: {}", &model_id[..8]);
let conn = conn.lock().map_err(|e| anyhow!("获取数据库连接失败: {}", e))?;
// 🚨 强制使用连接池,避免死锁
if !self.database.has_pool() {
return Err(anyhow!("连接池未启用,无法安全执行数据库操作"));
}
let pooled_conn = self.database.acquire_from_pool()
.map_err(|e| anyhow!("获取连接池连接失败: {}", e))?;
// 获取记录统计 // 获取记录统计
let mut stmt = conn.prepare( let mut stmt = pooled_conn.prepare(
r#" r#"
SELECT SELECT
COUNT(*) as total_records, COUNT(*) as total_records,
@ -593,7 +640,7 @@ impl OutfitImageRepository {
}).map_err(|e| anyhow!("查询记录统计失败: {}", e))?; }).map_err(|e| anyhow!("查询记录统计失败: {}", e))?;
// 获取图片统计 // 获取图片统计
let mut stmt = conn.prepare( let mut stmt = pooled_conn.prepare(
r#" r#"
SELECT SELECT
COUNT(*) as total_images, COUNT(*) as total_images,
@ -611,6 +658,7 @@ impl OutfitImageRepository {
)) ))
}).map_err(|e| anyhow!("查询图片统计失败: {}", e))?; }).map_err(|e| anyhow!("查询图片统计失败: {}", e))?;
println!("✅ 连接池查询穿搭统计完成: 记录{}条, 图片{}", total_records, total_images);
Ok(OutfitImageStats { Ok(OutfitImageStats {
total_records, total_records,
total_images, total_images,