feat(plugin): 字段正则校验 — validation.pattern 支持
Cargo.toml 新增 regex 依赖。validate_data 函数扩展支持 FieldValidation.pattern 正则校验,空值非必填字段跳过校验, 校验失败时返回自定义 message 或默认提示。
This commit is contained in:
@@ -24,3 +24,4 @@ async-trait = { workspace = true }
|
|||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
moka = { version = "0.12", features = ["sync"] }
|
moka = { version = "0.12", features = ["sync"] }
|
||||||
|
regex = "1"
|
||||||
|
|||||||
@@ -471,17 +471,40 @@ pub async fn resolve_entity_info_cached(
|
|||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 校验数据:检查 required 字段
|
/// 校验数据:检查 required 字段 + 正则校验
|
||||||
fn validate_data(data: &serde_json::Value, fields: &[PluginField]) -> AppResult<()> {
|
fn validate_data(data: &serde_json::Value, fields: &[PluginField]) -> AppResult<()> {
|
||||||
let obj = data.as_object().ok_or_else(|| {
|
let obj = data.as_object().ok_or_else(|| {
|
||||||
AppError::Validation("data 必须是 JSON 对象".to_string())
|
AppError::Validation("data 必须是 JSON 对象".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
for field in fields {
|
for field in fields {
|
||||||
|
let label = field.display_name.as_deref().unwrap_or(&field.name);
|
||||||
|
|
||||||
|
// required 检查
|
||||||
if field.required && !obj.contains_key(&field.name) {
|
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)));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,3 +575,46 @@ async fn validate_ref_entities(
|
|||||||
}
|
}
|
||||||
Ok(())
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user