feat(admin): 实现系统数据导入导出功能,支持提供商和模型批量配置

This commit is contained in:
fawney19
2026-04-11 21:39:04 +08:00
parent 801e16c988
commit a9f610fa69
36 changed files with 3247 additions and 409 deletions

View File

@@ -1 +1,126 @@
use serde::de::DeserializeOwned;
use serde_json::{Map, Value};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AdminJsonFieldState {
Missing,
Null,
Present,
}
impl AdminJsonFieldState {
pub(crate) fn is_present(self) -> bool {
!matches!(self, Self::Missing)
}
pub(crate) fn is_null(self) -> bool {
matches!(self, Self::Null)
}
}
#[derive(Debug, Clone)]
pub(crate) struct AdminJsonObjectPatch {
field_states: BTreeMap<String, AdminJsonFieldState>,
}
impl AdminJsonObjectPatch {
fn from_object(raw_payload: &Map<String, Value>) -> Self {
let field_states = raw_payload
.iter()
.map(|(key, value)| {
let state = if value.is_null() {
AdminJsonFieldState::Null
} else {
AdminJsonFieldState::Present
};
(key.clone(), state)
})
.collect();
Self { field_states }
}
pub(crate) fn state(&self, field: &str) -> AdminJsonFieldState {
self.field_states
.get(field)
.copied()
.unwrap_or(AdminJsonFieldState::Missing)
}
pub(crate) fn contains(&self, field: &str) -> bool {
self.state(field).is_present()
}
pub(crate) fn is_null(&self, field: &str) -> bool {
self.state(field).is_null()
}
}
#[derive(Debug, Clone)]
pub(crate) struct AdminTypedObjectPatch<T> {
fields: AdminJsonObjectPatch,
pub(crate) payload: T,
}
impl<T> AdminTypedObjectPatch<T>
where
T: DeserializeOwned,
{
pub(crate) fn from_object(raw_payload: Map<String, Value>) -> Result<Self, serde_json::Error> {
let fields = AdminJsonObjectPatch::from_object(&raw_payload);
let payload = serde_json::from_value(Value::Object(raw_payload))?;
Ok(Self { fields, payload })
}
pub(crate) fn contains(&self, field: &str) -> bool {
self.fields.contains(field)
}
pub(crate) fn is_null(&self, field: &str) -> bool {
self.fields.is_null(field)
}
pub(crate) fn into_parts(self) -> (AdminJsonObjectPatch, T) {
(self.fields, self.payload)
}
}
#[cfg(test)]
mod tests {
use super::{AdminJsonFieldState, AdminTypedObjectPatch};
use serde::Deserialize;
use serde_json::json;
#[derive(Debug, Deserialize)]
struct ExamplePatchPayload {
#[serde(default)]
description: Option<String>,
#[serde(default)]
enabled: Option<bool>,
}
#[test]
fn admin_typed_object_patch_tracks_field_presence() {
let patch = AdminTypedObjectPatch::<ExamplePatchPayload>::from_object(
json!({
"description": null,
"enabled": true,
})
.as_object()
.cloned()
.expect("object"),
)
.expect("patch");
assert_eq!(patch.payload.description, None);
assert_eq!(patch.payload.enabled, Some(true));
assert!(patch.contains("description"));
assert!(patch.is_null("description"));
assert!(patch.contains("enabled"));
assert!(!patch.is_null("enabled"));
assert_eq!(
patch.into_parts().0.state("missing_field"),
AdminJsonFieldState::Missing
);
}
}