feat(plugin): 批量操作端点 — batch_delete + batch_update
This commit is contained in:
@@ -4,7 +4,7 @@ use uuid::Uuid;
|
||||
use erp_core::error::{AppError, AppResult};
|
||||
use erp_core::events::EventBus;
|
||||
|
||||
use crate::data_dto::PluginDataResp;
|
||||
use crate::data_dto::{BatchActionReq, PluginDataResp};
|
||||
use crate::dynamic_table::{sanitize_identifier, DynamicTableManager};
|
||||
use crate::entity::plugin;
|
||||
use crate::entity::plugin_entity;
|
||||
@@ -372,6 +372,111 @@ impl PluginDataService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 批量操作 — batch_delete / batch_update
|
||||
pub async fn batch(
|
||||
plugin_id: Uuid,
|
||||
entity_name: &str,
|
||||
tenant_id: Uuid,
|
||||
operator_id: Uuid,
|
||||
req: BatchActionReq,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> AppResult<u64> {
|
||||
if req.ids.is_empty() {
|
||||
return Err(AppError::Validation("ids 不能为空".to_string()));
|
||||
}
|
||||
if req.ids.len() > 100 {
|
||||
return Err(AppError::Validation("批量操作上限 100 条".to_string()));
|
||||
}
|
||||
|
||||
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
||||
let ids: Vec<Uuid> = req
|
||||
.ids
|
||||
.iter()
|
||||
.map(|s| Uuid::parse_str(s))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| AppError::Validation("ids 中包含无效的 UUID".to_string()))?;
|
||||
|
||||
let affected = match req.action.as_str() {
|
||||
"batch_delete" => {
|
||||
let placeholders: Vec<String> = ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| format!("${}", i + 2))
|
||||
.collect();
|
||||
let sql = format!(
|
||||
"UPDATE \"{}\" SET deleted_at = NOW(), updated_at = NOW() \
|
||||
WHERE tenant_id = $1 AND id IN ({}) AND deleted_at IS NULL",
|
||||
info.table_name,
|
||||
placeholders.join(", ")
|
||||
);
|
||||
let mut values = vec![tenant_id.into()];
|
||||
for id in &ids {
|
||||
values.push((*id).into());
|
||||
}
|
||||
let result = db
|
||||
.execute(Statement::from_sql_and_values(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
sql,
|
||||
values,
|
||||
))
|
||||
.await?;
|
||||
result.rows_affected()
|
||||
}
|
||||
"batch_update" => {
|
||||
let update_data = req.data.ok_or_else(|| {
|
||||
AppError::Validation("batch_update 需要 data 字段".to_string())
|
||||
})?;
|
||||
let mut set_expr = "data".to_string();
|
||||
if let Some(obj) = update_data.as_object() {
|
||||
for key in obj.keys() {
|
||||
let clean_key = sanitize_identifier(key);
|
||||
set_expr = format!(
|
||||
"jsonb_set({}, '{{{}}}', $2::jsonb->'{}', true)",
|
||||
set_expr, clean_key, clean_key
|
||||
);
|
||||
}
|
||||
}
|
||||
let placeholders: Vec<String> = ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| format!("${}", i + 3))
|
||||
.collect();
|
||||
let sql = format!(
|
||||
"UPDATE \"{}\" SET data = {}, updated_at = NOW(), updated_by = $1, version = version + 1 \
|
||||
WHERE tenant_id = $1 AND id IN ({}) AND deleted_at IS NULL",
|
||||
info.table_name,
|
||||
set_expr,
|
||||
placeholders.join(", ")
|
||||
);
|
||||
let mut values = vec![operator_id.into()];
|
||||
values.push(
|
||||
serde_json::to_string(&update_data)
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
);
|
||||
for id in &ids {
|
||||
values.push((*id).into());
|
||||
}
|
||||
let result = db
|
||||
.execute(Statement::from_sql_and_values(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
sql,
|
||||
values,
|
||||
))
|
||||
.await?;
|
||||
result.rows_affected()
|
||||
}
|
||||
_ => {
|
||||
return Err(AppError::Validation(format!(
|
||||
"不支持的批量操作: {}",
|
||||
req.action
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(affected)
|
||||
}
|
||||
|
||||
/// 统计记录数(支持过滤和搜索)
|
||||
pub async fn count(
|
||||
plugin_id: Uuid,
|
||||
|
||||
Reference in New Issue
Block a user