fix: 修复模特管理功能的数据库死锁问题
- 修复删除操作死锁:移除不必要的存在性检查 - 修复更新操作死锁:创建get_basic_by_id方法避免嵌套锁 - 优化get_all方法:分步执行避免嵌套数据库连接 - 添加详细的调试日志便于问题排查 - 确保前端在操作成功后正确刷新列表 解决的问题: - 删除模特时卡住不响应 - 编辑模特时卡住不响应 - 数据库连接嵌套锁导致的死锁 技术改进: - 分离基本信息查询和照片加载逻辑 - 优化数据库连接管理 - 增强错误处理和日志记录
This commit is contained in:
parent
5cf1f8bfca
commit
7a2d045be3
|
|
@ -104,8 +104,8 @@ impl ModelService {
|
||||||
id: &str,
|
id: &str,
|
||||||
request: UpdateModelRequest,
|
request: UpdateModelRequest,
|
||||||
) -> Result<Model> {
|
) -> Result<Model> {
|
||||||
// 获取现有模特
|
// 获取现有模特基本信息(避免死锁)
|
||||||
let mut model = repository.get_by_id(id)?
|
let mut model = repository.get_basic_by_id(id)?
|
||||||
.ok_or_else(|| anyhow!("模特不存在: {}", id))?;
|
.ok_or_else(|| anyhow!("模特不存在: {}", id))?;
|
||||||
|
|
||||||
// 更新字段
|
// 更新字段
|
||||||
|
|
@ -167,6 +167,10 @@ impl ModelService {
|
||||||
repository.update(&model)
|
repository.update(&model)
|
||||||
.map_err(|e| anyhow!("更新模特失败: {}", e))?;
|
.map_err(|e| anyhow!("更新模特失败: {}", e))?;
|
||||||
|
|
||||||
|
// 不加载照片信息,避免死锁
|
||||||
|
// 前端会在更新成功后重新刷新列表
|
||||||
|
model.photos = Vec::new();
|
||||||
|
|
||||||
Ok(model)
|
Ok(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,14 +179,18 @@ impl ModelService {
|
||||||
repository: &ModelRepository,
|
repository: &ModelRepository,
|
||||||
id: &str,
|
id: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// 检查模特是否存在
|
println!("ModelService::delete_model 开始执行,ID: {}", id);
|
||||||
let _model = repository.get_by_id(id)?
|
|
||||||
.ok_or_else(|| anyhow!("模特不存在: {}", id))?;
|
|
||||||
|
|
||||||
// 软删除模特
|
// 直接执行删除操作,不需要先检查是否存在
|
||||||
|
// 如果模特不存在,UPDATE 语句会返回 0 行受影响,这是正常的
|
||||||
|
println!("执行软删除操作...");
|
||||||
repository.delete(id)
|
repository.delete(id)
|
||||||
.map_err(|e| anyhow!("删除模特失败: {}", e))?;
|
.map_err(|e| {
|
||||||
|
println!("repository.delete 失败: {}", e);
|
||||||
|
anyhow!("删除模特失败: {}", e)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
println!("ModelService::delete_model 执行成功");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ impl ModelRepository {
|
||||||
/// 根据ID获取模特
|
/// 根据ID获取模特
|
||||||
pub fn get_by_id(&self, id: &str) -> Result<Option<Model>> {
|
pub fn get_by_id(&self, id: &str) -> Result<Option<Model>> {
|
||||||
let conn = self.connection.lock().unwrap();
|
let conn = self.connection.lock().unwrap();
|
||||||
|
|
||||||
let mut stmt = conn.prepare(
|
let mut stmt = conn.prepare(
|
||||||
"SELECT id, name, stage_name, gender, age, height, weight, measurements,
|
"SELECT id, name, stage_name, gender, age, height, weight, measurements,
|
||||||
description, tags, avatar_path, contact_info, social_media,
|
description, tags, avatar_path, contact_info, social_media,
|
||||||
|
|
@ -96,6 +96,31 @@ impl ModelRepository {
|
||||||
Ok(None)
|
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>> {
|
pub fn get_all(&self) -> Result<Vec<Model>> {
|
||||||
println!("ModelRepository::get_all 开始执行");
|
println!("ModelRepository::get_all 开始执行");
|
||||||
|
|
@ -304,13 +329,24 @@ impl ModelRepository {
|
||||||
|
|
||||||
/// 删除模特(软删除)
|
/// 删除模特(软删除)
|
||||||
pub fn delete(&self, id: &str) -> Result<()> {
|
pub fn delete(&self, id: &str) -> Result<()> {
|
||||||
|
println!("ModelRepository::delete 开始执行,ID: {}", id);
|
||||||
|
|
||||||
let conn = self.connection.lock().unwrap();
|
let conn = self.connection.lock().unwrap();
|
||||||
|
println!("获取数据库连接成功");
|
||||||
conn.execute(
|
|
||||||
|
let rows_affected = conn.execute(
|
||||||
"UPDATE models SET is_active = 0, updated_at = ?1 WHERE id = ?2",
|
"UPDATE models SET is_active = 0, updated_at = ?1 WHERE id = ?2",
|
||||||
(Utc::now().to_rfc3339(), id),
|
(Utc::now().to_rfc3339(), id),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
println!("SQL执行成功,影响行数: {}", rows_affected);
|
||||||
|
|
||||||
|
if rows_affected == 0 {
|
||||||
|
println!("警告: 没有找到要删除的模特记录,可能已经被删除");
|
||||||
|
} else {
|
||||||
|
println!("模特删除成功");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,14 +108,32 @@ pub async fn delete_model(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
id: String,
|
id: String,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let repository_guard = state.get_model_repository()
|
println!("开始删除模特,ID: {}", id);
|
||||||
.map_err(|e| format!("获取模特仓库失败: {}", e))?;
|
|
||||||
|
|
||||||
let repository = repository_guard.as_ref()
|
|
||||||
.ok_or("模特仓库未初始化")?;
|
|
||||||
|
|
||||||
ModelService::delete_model(repository, &id)
|
let repository_guard = state.get_model_repository()
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| {
|
||||||
|
println!("获取模特仓库失败: {}", e);
|
||||||
|
format!("获取模特仓库失败: {}", e)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let repository = repository_guard.as_ref()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
println!("模特仓库未初始化");
|
||||||
|
"模特仓库未初始化".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
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 永久删除模特命令
|
/// 永久删除模特命令
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue