fix: resolve list_matching_results deserialization error
- Add custom deserializer for MatchingResultStatus to handle empty strings - Add serde(default) attributes to TemplateMatchingResultQueryOptions fields - Update frontend to send undefined instead of empty string for status filter - Fix 'unknown variant' and 'missing field' errors in template matching results Resolves issue where selecting 'All Status' filter caused command failures.
This commit is contained in:
parent
d5ef9851cd
commit
d3ab2aa284
|
|
@ -1,4 +1,4 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize, Deserializer};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
/// 模板匹配结果实体模型
|
/// 模板匹配结果实体模型
|
||||||
|
|
@ -28,7 +28,7 @@ pub struct TemplateMatchingResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 匹配结果状态
|
/// 匹配结果状态
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||||
pub enum MatchingResultStatus {
|
pub enum MatchingResultStatus {
|
||||||
/// 匹配成功
|
/// 匹配成功
|
||||||
Success,
|
Success,
|
||||||
|
|
@ -40,6 +40,27 @@ pub enum MatchingResultStatus {
|
||||||
Cancelled,
|
Cancelled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 自定义反序列化实现,处理空字符串和无效值
|
||||||
|
impl<'de> Deserialize<'de> for MatchingResultStatus {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
match s.as_str() {
|
||||||
|
"Success" => Ok(MatchingResultStatus::Success),
|
||||||
|
"PartialSuccess" => Ok(MatchingResultStatus::PartialSuccess),
|
||||||
|
"Failed" => Ok(MatchingResultStatus::Failed),
|
||||||
|
"Cancelled" => Ok(MatchingResultStatus::Cancelled),
|
||||||
|
"" => {
|
||||||
|
// 空字符串被视为无效值,返回错误让调用者处理
|
||||||
|
Err(serde::de::Error::custom("Empty string is not a valid MatchingResultStatus"))
|
||||||
|
}
|
||||||
|
_ => Err(serde::de::Error::unknown_variant(&s, &["Success", "PartialSuccess", "Failed", "Cancelled"])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for MatchingResultStatus {
|
impl Default for MatchingResultStatus {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Success
|
Self::Success
|
||||||
|
|
@ -294,16 +315,44 @@ pub struct CreateTemplateMatchingResultRequest {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 自定义反序列化函数,处理空字符串状态
|
||||||
|
fn deserialize_optional_status<'de, D>(deserializer: D) -> Result<Option<MatchingResultStatus>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let opt = Option::<String>::deserialize(deserializer)?;
|
||||||
|
match opt {
|
||||||
|
Some(s) if s.is_empty() => Ok(None), // 空字符串转换为 None
|
||||||
|
Some(s) => match s.as_str() {
|
||||||
|
"Success" => Ok(Some(MatchingResultStatus::Success)),
|
||||||
|
"PartialSuccess" => Ok(Some(MatchingResultStatus::PartialSuccess)),
|
||||||
|
"Failed" => Ok(Some(MatchingResultStatus::Failed)),
|
||||||
|
"Cancelled" => Ok(Some(MatchingResultStatus::Cancelled)),
|
||||||
|
_ => Err(serde::de::Error::unknown_variant(&s, &["Success", "PartialSuccess", "Failed", "Cancelled"])),
|
||||||
|
},
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 模板匹配结果查询选项
|
/// 模板匹配结果查询选项
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct TemplateMatchingResultQueryOptions {
|
pub struct TemplateMatchingResultQueryOptions {
|
||||||
|
#[serde(default)]
|
||||||
pub project_id: Option<String>,
|
pub project_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub template_id: Option<String>,
|
pub template_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub binding_id: Option<String>,
|
pub binding_id: Option<String>,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_optional_status")]
|
||||||
pub status: Option<MatchingResultStatus>,
|
pub status: Option<MatchingResultStatus>,
|
||||||
|
#[serde(default)]
|
||||||
pub limit: Option<u32>,
|
pub limit: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
pub offset: Option<u32>,
|
pub offset: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
pub search_keyword: Option<String>,
|
pub search_keyword: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub sort_by: Option<String>, // "created_at", "success_rate", "result_name"
|
pub sort_by: Option<String>, // "created_at", "success_rate", "result_name"
|
||||||
|
#[serde(default)]
|
||||||
pub sort_order: Option<String>, // "asc", "desc"
|
pub sort_order: Option<String>, // "asc", "desc"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export const TemplateMatchingResultManager: React.FC<TemplateMatchingResultManag
|
||||||
template_id: templateId,
|
template_id: templateId,
|
||||||
binding_id: bindingId,
|
binding_id: bindingId,
|
||||||
status: filters.status,
|
status: filters.status,
|
||||||
search_keyword: filters.searchKeyword,
|
search_keyword: filters.searchKeyword || undefined,
|
||||||
sort_by: filters.sortBy,
|
sort_by: filters.sortBy,
|
||||||
sort_order: filters.sortOrder,
|
sort_order: filters.sortOrder,
|
||||||
limit: pagination.pageSize,
|
limit: pagination.pageSize,
|
||||||
|
|
@ -206,7 +206,9 @@ export const TemplateMatchingResultManager: React.FC<TemplateMatchingResultManag
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
options={statusOptions}
|
options={statusOptions}
|
||||||
value={filters.status || ''}
|
value={filters.status || ''}
|
||||||
onChange={(value) => handleFilterChange({ status: value as MatchingResultStatus })}
|
onChange={(value) => handleFilterChange({
|
||||||
|
status: value === '' ? undefined : value as MatchingResultStatus
|
||||||
|
})}
|
||||||
placeholder="选择状态"
|
placeholder="选择状态"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue