From 314580243e247b5dbb323c4e01a57db631c91777 Mon Sep 17 00:00:00 2001 From: iven Date: Fri, 17 Apr 2026 10:37:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(plugin):=20=E5=AD=97=E6=AE=B5=E6=AD=A3?= =?UTF-8?q?=E5=88=99=E6=A0=A1=E9=AA=8C=20=E2=80=94=20validation.pattern=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo.toml 新增 regex 依赖。validate_data 函数扩展支持 FieldValidation.pattern 正则校验,空值非必填字段跳过校验, 校验失败时返回自定义 message 或默认提示。 --- crates/erp-plugin/Cargo.toml | 1 + crates/erp-plugin/src/data_service.rs | 70 ++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/crates/erp-plugin/Cargo.toml b/crates/erp-plugin/Cargo.toml index 3171a29..873a2bc 100644 --- a/crates/erp-plugin/Cargo.toml +++ b/crates/erp-plugin/Cargo.toml @@ -24,3 +24,4 @@ async-trait = { workspace = true } sha2 = { workspace = true } base64 = "0.22" moka = { version = "0.12", features = ["sync"] } +regex = "1" diff --git a/crates/erp-plugin/src/data_service.rs b/crates/erp-plugin/src/data_service.rs index 0d83941..f84eb76 100644 --- a/crates/erp-plugin/src/data_service.rs +++ b/crates/erp-plugin/src/data_service.rs @@ -471,17 +471,40 @@ pub async fn resolve_entity_info_cached( Ok(info) } -/// 校验数据:检查 required 字段 +/// 校验数据:检查 required 字段 + 正则校验 fn validate_data(data: &serde_json::Value, fields: &[PluginField]) -> AppResult<()> { let obj = data.as_object().ok_or_else(|| { AppError::Validation("data 必须是 JSON 对象".to_string()) })?; + for field in fields { + let label = field.display_name.as_deref().unwrap_or(&field.name); + + // required 检查 if field.required && !obj.contains_key(&field.name) { - let label = field.display_name.as_deref().unwrap_or(&field.name); return Err(AppError::Validation(format!("字段 '{}' 不能为空", label))); } + + // 正则校验 + if let Some(validation) = &field.validation { + if let Some(pattern) = &validation.pattern { + if let Some(val) = obj.get(&field.name) { + let str_val = val.as_str().unwrap_or(""); + if !str_val.is_empty() { + let re = regex::Regex::new(pattern) + .map_err(|e| AppError::Internal(format!("正则表达式编译失败: {}", e)))?; + if !re.is_match(str_val) { + let default_msg = format!("字段 '{}' 格式不正确", label); + let msg = validation.message.as_deref() + .unwrap_or(&default_msg); + return Err(AppError::Validation(msg.to_string())); + } + } + } + } + } } + Ok(()) } @@ -552,3 +575,46 @@ async fn validate_ref_entities( } Ok(()) } + +#[cfg(test)] +mod validate_tests { + use super::*; + use crate::manifest::{FieldValidation, PluginField, PluginFieldType}; + + fn make_field(name: &str, pattern: Option<&str>, message: Option<&str>) -> PluginField { + PluginField { + name: name.to_string(), + field_type: PluginFieldType::String, + required: false, + validation: pattern.map(|p| FieldValidation { + pattern: Some(p.to_string()), + message: message.map(|m| m.to_string()), + }), + ..PluginField::default_for_field() + } + } + + #[test] + fn validate_phone_pattern_rejects_invalid() { + let fields = vec![make_field("phone", Some("^1[3-9]\\d{9}$"), Some("手机号格式不正确"))]; + let data = serde_json::json!({"phone": "1234"}); + let result = validate_data(&data, &fields); + assert!(result.is_err()); + } + + #[test] + fn validate_phone_pattern_accepts_valid() { + let fields = vec![make_field("phone", Some("^1[3-9]\\d{9}$"), Some("手机号格式不正确"))]; + let data = serde_json::json!({"phone": "13812345678"}); + let result = validate_data(&data, &fields); + assert!(result.is_ok()); + } + + #[test] + fn validate_empty_optional_field_skips_pattern() { + let fields = vec![make_field("phone", Some("^1[3-9]\\d{9}$"), None)]; + let data = serde_json::json!({"phone": ""}); + let result = validate_data(&data, &fields); + assert!(result.is_ok()); + } +}