feat(plugin): 外键校验 — ref_entity 字段验证引用记录存在性
新增 validate_ref_entities 异步函数,在 create/update 时检查 ref_entity 字段指向的记录是否存在于对应动态表中。自引用 场景下 create 跳过校验,update 跳过自身引用。
This commit is contained in:
@@ -28,6 +28,7 @@ impl PluginDataService {
|
|||||||
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
||||||
let fields = info.fields()?;
|
let fields = info.fields()?;
|
||||||
validate_data(&data, &fields)?;
|
validate_data(&data, &fields)?;
|
||||||
|
validate_ref_entities(&data, &fields, entity_name, plugin_id, tenant_id, db, true, None).await?;
|
||||||
|
|
||||||
let (sql, values) =
|
let (sql, values) =
|
||||||
DynamicTableManager::build_insert_sql(&info.table_name, tenant_id, operator_id, &data);
|
DynamicTableManager::build_insert_sql(&info.table_name, tenant_id, operator_id, &data);
|
||||||
@@ -206,6 +207,7 @@ impl PluginDataService {
|
|||||||
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
||||||
let fields = info.fields()?;
|
let fields = info.fields()?;
|
||||||
validate_data(&data, &fields)?;
|
validate_data(&data, &fields)?;
|
||||||
|
validate_ref_entities(&data, &fields, entity_name, plugin_id, tenant_id, db, false, Some(id)).await?;
|
||||||
|
|
||||||
let (sql, values) = DynamicTableManager::build_update_sql(
|
let (sql, values) = DynamicTableManager::build_update_sql(
|
||||||
&info.table_name,
|
&info.table_name,
|
||||||
@@ -482,3 +484,71 @@ fn validate_data(data: &serde_json::Value, fields: &[PluginField]) -> AppResult<
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 校验外键引用 — 检查 ref_entity 字段指向的记录是否存在
|
||||||
|
async fn validate_ref_entities(
|
||||||
|
data: &serde_json::Value,
|
||||||
|
fields: &[PluginField],
|
||||||
|
current_entity: &str,
|
||||||
|
plugin_id: Uuid,
|
||||||
|
tenant_id: Uuid,
|
||||||
|
db: &sea_orm::DatabaseConnection,
|
||||||
|
is_create: bool,
|
||||||
|
record_id: Option<Uuid>,
|
||||||
|
) -> AppResult<()> {
|
||||||
|
let obj = data.as_object().ok_or_else(|| {
|
||||||
|
AppError::Validation("data 必须是 JSON 对象".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for field in fields {
|
||||||
|
let Some(ref_entity_name) = &field.ref_entity else { continue };
|
||||||
|
let Some(val) = obj.get(&field.name) else { continue };
|
||||||
|
let str_val = val.as_str().unwrap_or("").trim().to_string();
|
||||||
|
|
||||||
|
if str_val.is_empty() && !field.required { continue; }
|
||||||
|
if str_val.is_empty() { continue; }
|
||||||
|
|
||||||
|
let ref_id = Uuid::parse_str(&str_val).map_err(|_| {
|
||||||
|
AppError::Validation(format!(
|
||||||
|
"字段 '{}' 的值 '{}' 不是有效的 UUID",
|
||||||
|
field.display_name.as_deref().unwrap_or(&field.name),
|
||||||
|
str_val
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 自引用 + create:跳过(记录尚未存在)
|
||||||
|
if ref_entity_name == current_entity && is_create {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 自引用 + update:检查是否引用自身
|
||||||
|
if ref_entity_name == current_entity && !is_create {
|
||||||
|
if let Some(rid) = record_id {
|
||||||
|
if ref_id == rid { continue; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询被引用记录是否存在
|
||||||
|
let manifest_id = resolve_manifest_id(plugin_id, tenant_id, db).await?;
|
||||||
|
let ref_table = DynamicTableManager::table_name(&manifest_id, ref_entity_name);
|
||||||
|
|
||||||
|
let check_sql = format!(
|
||||||
|
"SELECT 1 as check_result FROM \"{}\" WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL LIMIT 1",
|
||||||
|
ref_table
|
||||||
|
);
|
||||||
|
#[derive(FromQueryResult)]
|
||||||
|
struct ExistsCheck { check_result: Option<i32> }
|
||||||
|
let result = ExistsCheck::find_by_statement(Statement::from_sql_and_values(
|
||||||
|
sea_orm::DatabaseBackend::Postgres,
|
||||||
|
check_sql,
|
||||||
|
[ref_id.into(), tenant_id.into()],
|
||||||
|
)).one(db).await?;
|
||||||
|
|
||||||
|
if result.is_none() {
|
||||||
|
return Err(AppError::Validation(format!(
|
||||||
|
"引用的 {} 记录不存在(ID: {})",
|
||||||
|
ref_entity_name, ref_id
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user