NAC_Blockchain/nac-ai-compliance/src/report_generator.rs

438 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 合规报告生成器模块
//!
//! 实现报告生成、存储、查询和导出
use crate::compliance_layer::*;
use crate::error::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
/// 报告生成器
pub struct ReportGenerator {
/// 报告存储路径
storage_path: PathBuf,
/// 报告缓存
cache: HashMap<String, ComplianceReport>,
}
impl ReportGenerator {
/// 创建新的报告生成器
pub fn new() -> Self {
Self {
storage_path: std::env::temp_dir().join("nac_compliance_reports"),
cache: HashMap::new(),
}
}
/// 设置存储路径
pub fn with_storage_path(mut self, path: PathBuf) -> Self {
self.storage_path = path;
self
}
/// 生成报告
pub fn generate(&self, results: &[ComplianceResult]) -> Result<ComplianceReport> {
let report_id = self.generate_report_id();
// 计算总体状态
let overall_status = self.calculate_overall_status(results);
// 计算总体风险等级
let overall_risk = self.calculate_overall_risk(results);
// 计算平均置信度
let avg_confidence = if results.is_empty() {
0.0
} else {
results.iter().map(|r| r.confidence).sum::<f64>() / results.len() as f64
};
// 收集所有问题
let all_issues: Vec<ComplianceIssue> = results
.iter()
.flat_map(|r| r.issues.clone())
.collect();
// 收集所有建议
let all_recommendations: Vec<String> = results
.iter()
.flat_map(|r| r.recommendations.clone())
.collect();
// 生成摘要
let summary = self.generate_summary(results);
Ok(ComplianceReport {
id: report_id,
results: results.to_vec(),
overall_status,
overall_risk,
average_confidence: avg_confidence,
total_issues: all_issues.len(),
total_recommendations: all_recommendations.len(),
summary,
generated_at: chrono::Utc::now(),
})
}
/// 保存报告
pub fn save(&mut self, report: &ComplianceReport) -> Result<()> {
// 创建存储目录
std::fs::create_dir_all(&self.storage_path)?;
// 序列化报告
let json = serde_json::to_string_pretty(report)?;
// 写入文件
let file_path = self.storage_path.join(format!("{}.json", report.id));
std::fs::write(&file_path, json)?;
// 添加到缓存
self.cache.insert(report.id.clone(), report.clone());
Ok(())
}
/// 加载报告
pub fn load(&mut self, report_id: &str) -> Result<ComplianceReport> {
// 先检查缓存
if let Some(report) = self.cache.get(report_id) {
return Ok(report.clone());
}
// 从文件加载
let file_path = self.storage_path.join(format!("{}.json", report_id));
let json = std::fs::read_to_string(&file_path)?;
let report: ComplianceReport = serde_json::from_str(&json)?;
// 添加到缓存
self.cache.insert(report_id.to_string(), report.clone());
Ok(report)
}
/// 查询报告
pub fn query(&self, filter: ReportFilter) -> Result<Vec<ComplianceReport>> {
let mut reports = Vec::new();
// 遍历存储目录
if !self.storage_path.exists() {
return Ok(reports);
}
for entry in std::fs::read_dir(&self.storage_path)? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) != Some("json") {
continue;
}
// 读取报告
let json = std::fs::read_to_string(&path)?;
let report: ComplianceReport = serde_json::from_str(&json)?;
// 应用过滤器
if filter.matches(&report) {
reports.push(report);
}
}
Ok(reports)
}
/// 导出报告
pub fn export(&self, report: &ComplianceReport, format: ExportFormat) -> Result<Vec<u8>> {
match format {
ExportFormat::Json => {
let json = serde_json::to_string_pretty(report)?;
Ok(json.into_bytes())
}
ExportFormat::Csv => {
self.export_csv(report)
}
ExportFormat::Pdf => {
// 简化实现返回JSON
// 实际实现需要使用PDF生成库
let json = serde_json::to_string_pretty(report)?;
Ok(json.into_bytes())
}
ExportFormat::Html => {
self.export_html(report)
}
}
}
/// 导出为CSV
fn export_csv(&self, report: &ComplianceReport) -> Result<Vec<u8>> {
let mut csv = String::new();
// 表头
csv.push_str("Layer,Status,Confidence,Risk Level,Issues,Recommendations\n");
// 数据行
for result in &report.results {
csv.push_str(&format!(
"{},{:?},{:.2},{:?},{},{}\n",
result.layer.name(),
result.status,
result.confidence,
result.risk_level,
result.issues.len(),
result.recommendations.len()
));
}
Ok(csv.into_bytes())
}
/// 导出为HTML
fn export_html(&self, report: &ComplianceReport) -> Result<Vec<u8>> {
let mut html = String::new();
html.push_str("<!DOCTYPE html>\n");
html.push_str("<html>\n<head>\n");
html.push_str("<meta charset=\"UTF-8\">\n");
html.push_str("<title>合规报告</title>\n");
html.push_str("<style>\n");
html.push_str("body { font-family: Arial, sans-serif; margin: 20px; }\n");
html.push_str("table { border-collapse: collapse; width: 100%; }\n");
html.push_str("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n");
html.push_str("th { background-color: #4CAF50; color: white; }\n");
html.push_str("</style>\n");
html.push_str("</head>\n<body>\n");
html.push_str(&format!("<h1>合规报告 #{}</h1>\n", report.id));
html.push_str(&format!("<p>生成时间: {}</p>\n", report.generated_at));
html.push_str(&format!("<p>总体状态: {:?}</p>\n", report.overall_status));
html.push_str(&format!("<p>总体风险: {:?}</p>\n", report.overall_risk));
html.push_str(&format!("<p>平均置信度: {:.2}</p>\n", report.average_confidence));
html.push_str("<h2>验证结果</h2>\n");
html.push_str("<table>\n");
html.push_str("<tr><th>层级</th><th>状态</th><th>置信度</th><th>风险等级</th><th>问题数</th><th>建议数</th></tr>\n");
for result in &report.results {
html.push_str(&format!(
"<tr><td>{}</td><td>{:?}</td><td>{:.2}</td><td>{:?}</td><td>{}</td><td>{}</td></tr>\n",
result.layer.name(),
result.status,
result.confidence,
result.risk_level,
result.issues.len(),
result.recommendations.len()
));
}
html.push_str("</table>\n");
html.push_str("</body>\n</html>");
Ok(html.into_bytes())
}
/// 生成报告ID
fn generate_report_id(&self) -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
format!("RPT{}", timestamp)
}
/// 计算总体状态
fn calculate_overall_status(&self, results: &[ComplianceResult]) -> ComplianceStatus {
if results.iter().any(|r| r.status == ComplianceStatus::Failed) {
ComplianceStatus::Failed
} else if results.iter().any(|r| r.status == ComplianceStatus::ManualReview) {
ComplianceStatus::ManualReview
} else if results.iter().any(|r| r.status == ComplianceStatus::ConditionalPass) {
ComplianceStatus::ConditionalPass
} else if results.iter().all(|r| r.status == ComplianceStatus::Passed) {
ComplianceStatus::Passed
} else {
ComplianceStatus::Pending
}
}
/// 计算总体风险
fn calculate_overall_risk(&self, results: &[ComplianceResult]) -> RiskLevel {
results
.iter()
.map(|r| r.risk_level)
.max()
.unwrap_or(RiskLevel::Low)
}
/// 生成摘要
fn generate_summary(&self, results: &[ComplianceResult]) -> String {
let passed = results.iter().filter(|r| r.status == ComplianceStatus::Passed).count();
let failed = results.iter().filter(|r| r.status == ComplianceStatus::Failed).count();
let manual = results.iter().filter(|r| r.status == ComplianceStatus::ManualReview).count();
format!(
"共验证{}个层级,通过{}个,失败{}个,需人工审核{}",
results.len(),
passed,
failed,
manual
)
}
}
impl Default for ReportGenerator {
fn default() -> Self {
Self::new()
}
}
/// 合规报告
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceReport {
/// 报告ID
pub id: String,
/// 验证结果列表
pub results: Vec<ComplianceResult>,
/// 总体状态
pub overall_status: ComplianceStatus,
/// 总体风险等级
pub overall_risk: RiskLevel,
/// 平均置信度
pub average_confidence: f64,
/// 总问题数
pub total_issues: usize,
/// 总建议数
pub total_recommendations: usize,
/// 摘要
pub summary: String,
/// 生成时间
pub generated_at: chrono::DateTime<chrono::Utc>,
}
/// 报告过滤器
#[derive(Debug, Clone, Default)]
pub struct ReportFilter {
/// 状态过滤
pub status: Option<ComplianceStatus>,
/// 风险等级过滤
pub risk_level: Option<RiskLevel>,
/// 开始时间
pub start_time: Option<chrono::DateTime<chrono::Utc>>,
/// 结束时间
pub end_time: Option<chrono::DateTime<chrono::Utc>>,
}
impl ReportFilter {
/// 检查报告是否匹配过滤器
pub fn matches(&self, report: &ComplianceReport) -> bool {
if let Some(status) = self.status {
if report.overall_status != status {
return false;
}
}
if let Some(risk_level) = self.risk_level {
if report.overall_risk != risk_level {
return false;
}
}
if let Some(start_time) = self.start_time {
if report.generated_at < start_time {
return false;
}
}
if let Some(end_time) = self.end_time {
if report.generated_at > end_time {
return false;
}
}
true
}
}
/// 导出格式
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExportFormat {
/// JSON格式
Json,
/// CSV格式
Csv,
/// PDF格式
Pdf,
/// HTML格式
Html,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_report_generation() {
let generator = ReportGenerator::new();
let results = vec![
ComplianceResult {
layer: ComplianceLayer::IdentityVerification,
status: ComplianceStatus::Passed,
confidence: 0.9,
risk_level: RiskLevel::Low,
details: "Test".to_string(),
issues: vec![],
recommendations: vec![],
timestamp: chrono::Utc::now(),
}
];
let report = generator.generate(&results).unwrap();
assert_eq!(report.results.len(), 1);
assert_eq!(report.overall_status, ComplianceStatus::Passed);
}
#[test]
fn test_report_export_json() {
let generator = ReportGenerator::new();
let results = vec![
ComplianceResult {
layer: ComplianceLayer::IdentityVerification,
status: ComplianceStatus::Passed,
confidence: 0.9,
risk_level: RiskLevel::Low,
details: "Test".to_string(),
issues: vec![],
recommendations: vec![],
timestamp: chrono::Utc::now(),
}
];
let report = generator.generate(&results).unwrap();
let exported = generator.export(&report, ExportFormat::Json).unwrap();
assert!(!exported.is_empty());
}
#[test]
fn test_report_filter() {
let filter = ReportFilter {
status: Some(ComplianceStatus::Passed),
..Default::default()
};
let report = ComplianceReport {
id: "test".to_string(),
results: vec![],
overall_status: ComplianceStatus::Passed,
overall_risk: RiskLevel::Low,
average_confidence: 0.9,
total_issues: 0,
total_recommendations: 0,
summary: "Test".to_string(),
generated_at: chrono::Utc::now(),
};
assert!(filter.matches(&report));
}
}