diff --git a/crates/erp-plugin/src/data_service.rs b/crates/erp-plugin/src/data_service.rs index fd75f27..0d83941 100644 --- a/crates/erp-plugin/src/data_service.rs +++ b/crates/erp-plugin/src/data_service.rs @@ -28,6 +28,7 @@ impl PluginDataService { let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?; let fields = info.fields()?; validate_data(&data, &fields)?; + validate_ref_entities(&data, &fields, entity_name, plugin_id, tenant_id, db, true, None).await?; let (sql, values) = 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 fields = info.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( &info.table_name, @@ -482,3 +484,71 @@ fn validate_data(data: &serde_json::Value, fields: &[PluginField]) -> AppResult< } 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, +) -> 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 } + 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(()) +}