优化 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 }
}
/// 初始化会话相关数据表
/// 初始化会话相关数据表(强制使用连接池)
pub fn initialize_tables(&self) -> Result<()> {
let conn = self.database.get_connection();
let conn = conn.lock().unwrap();
println!("🏊 使用连接池初始化会话表...");
// 🚨 强制使用连接池,避免死锁
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 (
id TEXT PRIMARY KEY,
title TEXT,
@ -41,7 +48,7 @@ impl ConversationRepository {
)?;
// 创建消息表
conn.execute(
pooled_conn.execute(
"CREATE TABLE IF NOT EXISTS conversation_messages (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
@ -55,18 +62,19 @@ impl ConversationRepository {
)?;
// 创建索引以提高查询性能
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_messages_session_timestamp
pooled_conn.execute(
"CREATE INDEX IF NOT EXISTS idx_messages_session_timestamp
ON conversation_messages (session_id, timestamp)",
[],
)?;
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_sessions_active_updated
pooled_conn.execute(
"CREATE INDEX IF NOT EXISTS idx_sessions_active_updated
ON conversation_sessions (is_active, updated_at)",
[],
)?;
println!("✅ 会话表初始化完成");
Ok(())
}

View File

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