fix(admin): 修复系统配置导入的多项兼容性问题

- 引入 serde_path_to_error,反序列化失败时返回精确字段路径
- 为所有浮点字段新增数字字符串兼容反序列化器,支持 Python 序列化格式
- 修复 OAuth provider key 导入:正确写入加密的 api_key 和 auth_config
- 优化代理节点跳过提示,区分手动 URL 代理与 node_id 引用代理
- 新增测试覆盖上述场景
This commit is contained in:
fawney19
2026-04-11 23:10:25 +08:00
parent a9f610fa69
commit 7c5bb7f383
9 changed files with 656 additions and 25 deletions
@@ -1,4 +1,6 @@
use crate::handlers::admin::shared::AdminTypedObjectPatch;
use crate::handlers::admin::shared::{
deserialize_optional_f64_from_number_or_string, AdminTypedObjectPatch,
};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
@@ -108,7 +110,10 @@ pub(crate) struct AdminProviderCreateRequest {
pub(crate) website: Option<String>,
#[serde(default)]
pub(crate) billing_type: Option<String>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) monthly_quota_usd: Option<f64>,
#[serde(default)]
pub(crate) quota_reset_day: Option<u64>,
@@ -128,9 +133,15 @@ pub(crate) struct AdminProviderCreateRequest {
pub(crate) max_retries: Option<i32>,
#[serde(default)]
pub(crate) proxy: Option<serde_json::Value>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) stream_first_byte_timeout: Option<f64>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) request_timeout: Option<f64>,
#[serde(default)]
pub(crate) pool_advanced: Option<serde_json::Value>,
@@ -154,7 +165,10 @@ pub(crate) struct AdminProviderUpdateRequest {
pub(crate) website: Option<String>,
#[serde(default)]
pub(crate) billing_type: Option<String>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) monthly_quota_usd: Option<f64>,
#[serde(default)]
pub(crate) quota_reset_day: Option<u64>,
@@ -174,9 +188,15 @@ pub(crate) struct AdminProviderUpdateRequest {
pub(crate) max_retries: Option<i32>,
#[serde(default)]
pub(crate) proxy: Option<serde_json::Value>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) stream_first_byte_timeout: Option<f64>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) request_timeout: Option<f64>,
#[serde(default)]
pub(crate) pool_advanced: Option<serde_json::Value>,
@@ -207,7 +227,10 @@ pub(crate) struct AdminProviderModelCreateRequest {
#[serde(default)]
pub(crate) provider_model_mappings: Option<serde_json::Value>,
pub(crate) global_model_id: String,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) price_per_request: Option<f64>,
#[serde(default)]
pub(crate) tiered_pricing: Option<serde_json::Value>,
@@ -233,7 +256,10 @@ pub(crate) struct AdminProviderModelUpdateRequest {
pub(crate) provider_model_mappings: Option<serde_json::Value>,
#[serde(default)]
pub(crate) global_model_id: Option<String>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) price_per_request: Option<f64>,
#[serde(default)]
pub(crate) tiered_pricing: Option<serde_json::Value>,
@@ -266,6 +292,9 @@ pub(crate) struct AdminImportProviderModelsRequest {
pub(crate) model_ids: Vec<String>,
#[serde(default)]
pub(crate) tiered_pricing: Option<serde_json::Value>,
#[serde(default)]
#[serde(
default,
deserialize_with = "deserialize_optional_f64_from_number_or_string"
)]
pub(crate) price_per_request: Option<f64>,
}