fix: 修复模特管理功能的数据库死锁问题

- 修复删除操作死锁:移除不必要的存在性检查
- 修复更新操作死锁:创建get_basic_by_id方法避免嵌套锁
- 优化get_all方法:分步执行避免嵌套数据库连接
- 添加详细的调试日志便于问题排查
- 确保前端在操作成功后正确刷新列表

解决的问题:
- 删除模特时卡住不响应
- 编辑模特时卡住不响应
- 数据库连接嵌套锁导致的死锁

技术改进:
- 分离基本信息查询和照片加载逻辑
- 优化数据库连接管理
- 增强错误处理和日志记录
This commit is contained in:
imeepos 2025-07-14 10:10:35 +08:00
parent 5cf1f8bfca
commit 7a2d045be3
3 changed files with 79 additions and 17 deletions

View File

@ -104,8 +104,8 @@ impl ModelService {
id: &str,
request: UpdateModelRequest,
) -> Result<Model> {
// 获取现有模特
let mut model = repository.get_by_id(id)?
// 获取现有模特基本信息(避免死锁)
let mut model = repository.get_basic_by_id(id)?
.ok_or_else(|| anyhow!("模特不存在: {}", id))?;
// 更新字段
@ -167,6 +167,10 @@ impl ModelService {
repository.update(&model)
.map_err(|e| anyhow!("更新模特失败: {}", e))?;
// 不加载照片信息,避免死锁
// 前端会在更新成功后重新刷新列表
model.photos = Vec::new();
Ok(model)
}
@ -175,14 +179,18 @@ impl ModelService {
repository: &ModelRepository,
id: &str,
) -> Result<()> {
// 检查模特是否存在
let _model = repository.get_by_id(id)?
.ok_or_else(|| anyhow!("模特不存在: {}", id))?;
println!("ModelService::delete_model 开始执行ID: {}", id);
// 软删除模特
// 直接执行删除操作,不需要先检查是否存在
// 如果模特不存在UPDATE 语句会返回 0 行受影响,这是正常的
println!("执行软删除操作...");
repository.delete(id)
.map_err(|e| anyhow!("删除模特失败: {}", e))?;
.map_err(|e| {
println!("repository.delete 失败: {}", e);
anyhow!("删除模特失败: {}", e)
})?;
println!("ModelService::delete_model 执行成功");
Ok(())
}

View File

@ -96,6 +96,31 @@ impl ModelRepository {
Ok(None)
}
/// 根据ID获取模特基本信息不加载照片避免死锁
pub fn get_basic_by_id(&self, id: &str) -> Result<Option<Model>> {
let conn = self.connection.lock().unwrap();
let mut stmt = conn.prepare(
"SELECT id, name, stage_name, gender, age, height, weight, measurements,
description, tags, avatar_path, contact_info, social_media,
status, rating, created_at, updated_at, is_active
FROM models WHERE id = ?1"
)?;
let model_iter = stmt.query_map([id], |row| {
self.row_to_model(row)
})?;
for model in model_iter {
let mut model = model?;
// 不加载照片信息,避免死锁
model.photos = Vec::new();
return Ok(Some(model));
}
Ok(None)
}
/// 获取所有模特
pub fn get_all(&self) -> Result<Vec<Model>> {
println!("ModelRepository::get_all 开始执行");
@ -304,13 +329,24 @@ impl ModelRepository {
/// 删除模特(软删除)
pub fn delete(&self, id: &str) -> Result<()> {
let conn = self.connection.lock().unwrap();
println!("ModelRepository::delete 开始执行ID: {}", id);
conn.execute(
let conn = self.connection.lock().unwrap();
println!("获取数据库连接成功");
let rows_affected = conn.execute(
"UPDATE models SET is_active = 0, updated_at = ?1 WHERE id = ?2",
(Utc::now().to_rfc3339(), id),
)?;
println!("SQL执行成功影响行数: {}", rows_affected);
if rows_affected == 0 {
println!("警告: 没有找到要删除的模特记录,可能已经被删除");
} else {
println!("模特删除成功");
}
Ok(())
}

View File

@ -108,14 +108,32 @@ pub async fn delete_model(
state: State<'_, AppState>,
id: String,
) -> Result<(), String> {
println!("开始删除模特ID: {}", id);
let repository_guard = state.get_model_repository()
.map_err(|e| format!("获取模特仓库失败: {}", e))?;
.map_err(|e| {
println!("获取模特仓库失败: {}", e);
format!("获取模特仓库失败: {}", e)
})?;
let repository = repository_guard.as_ref()
.ok_or("模特仓库未初始化")?;
.ok_or_else(|| {
println!("模特仓库未初始化");
"模特仓库未初始化".to_string()
})?;
ModelService::delete_model(repository, &id)
.map_err(|e| e.to_string())
println!("调用 ModelService::delete_model");
let result = ModelService::delete_model(repository, &id)
.map_err(|e| {
println!("删除模特失败: {}", e);
e.to_string()
});
if result.is_ok() {
println!("模特删除成功");
}
result
}
/// 永久删除模特命令