20260616 增加产品UI查询服务

This commit is contained in:
2026-06-17 10:16:04 +08:00
parent 4e6bd687db
commit 6ef2c98890
20 changed files with 7526 additions and 426 deletions

View File

@@ -0,0 +1,139 @@
use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::query_db::QueryDbResult;
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArtifactFileSummary {
pub path: String,
pub size_bytes: Option<u64>,
pub modified_unix_secs: Option<u64>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct QueryAuditArtifactSummary {
pub status: Option<String>,
pub events_path: Option<String>,
pub events_count: Option<u64>,
pub events_sha256: Option<String>,
pub writer_version: Option<u64>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArtifactManifestSummary {
pub files: BTreeMap<String, ArtifactFileSummary>,
pub query_audit: Option<QueryAuditArtifactSummary>,
pub cir_counts_available: bool,
pub validation_events_indexed: bool,
}
impl ArtifactManifestSummary {
pub fn artifact_paths(&self) -> BTreeMap<String, String> {
self.files
.iter()
.map(|(name, item)| (name.clone(), item.path.clone()))
.collect()
}
}
pub fn build_artifact_manifest(
run_dir: &Path,
query_audit: Option<&Value>,
) -> QueryDbResult<ArtifactManifestSummary> {
let mut manifest = ArtifactManifestSummary {
query_audit: query_audit.map(query_audit_summary),
cir_counts_available: false,
validation_events_indexed: false,
..ArtifactManifestSummary::default()
};
for name in [
"report.json",
"input.cir",
"result.cir",
"result.ccr",
"stage-timing.json",
"run-summary.json",
] {
let path = run_dir.join(name);
if path.exists() {
manifest
.files
.insert(name.to_string(), file_summary(&path)?);
}
}
if let Some(events_path) = manifest
.query_audit
.as_ref()
.and_then(|query_audit| query_audit.events_path.as_ref())
{
let path = sidecar_path(run_dir, events_path);
if path.exists() {
manifest
.files
.insert("validationEvents".to_string(), file_summary(&path)?);
}
}
Ok(manifest)
}
fn file_summary(path: &Path) -> QueryDbResult<ArtifactFileSummary> {
let metadata = fs::metadata(path)?;
let modified_unix_secs = metadata
.modified()
.ok()
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
.map(|duration| duration.as_secs());
Ok(ArtifactFileSummary {
path: path.display().to_string(),
size_bytes: Some(metadata.len()),
modified_unix_secs,
})
}
fn query_audit_summary(value: &Value) -> QueryAuditArtifactSummary {
QueryAuditArtifactSummary {
status: json_str(value, &["status"]).map(str::to_string),
events_path: json_str(value, &["eventsPath"])
.or_else(|| json_str(value, &["events_path"]))
.map(str::to_string),
events_count: json_u64(value, &["eventsCount"])
.or_else(|| json_u64(value, &["events_count"])),
events_sha256: json_str(value, &["eventsSha256"])
.or_else(|| json_str(value, &["events_sha256"]))
.map(str::to_string),
writer_version: json_u64(value, &["writerVersion"])
.or_else(|| json_u64(value, &["writer_version"])),
}
}
pub fn sidecar_path(run_dir: &Path, path: &str) -> PathBuf {
let path = PathBuf::from(path);
if path.is_absolute() {
path
} else {
run_dir.join(path)
}
}
fn json_str<'a>(value: &'a Value, path: &[&str]) -> Option<&'a str> {
let mut current = value;
for key in path {
current = current.get(*key)?;
}
current.as_str()
}
fn json_u64(value: &Value, path: &[&str]) -> Option<u64> {
let mut current = value;
for key in path {
current = current.get(*key)?;
}
current.as_u64()
}