mod api;
mod engine;
mod numerics;
mod models;
use actix_web::{web, App, HttpServer, HttpResponse, middleware};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct TenderScanRequest {
pub tender_id: String,
pub participants: Vec<Participant>,
pub reference_prices: Vec<f64>,
pub scan_depth: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct Participant {
pub company_id: String,
pub bid_amount: f64,
pub currency: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct TenderScanResult {
pub tender_id: String,
pub risk_score: f64,
pub flags: Vec<Flag>,
pub method: String,
pub confidence: f64,
pub scan_time_ms: u64,
}
#[derive(Debug, Serialize)]
pub struct Flag {
pub flag_type: String,
pub severity: String,
pub description: String,
}
async fn health() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({
"service": "bidradar",
"version": "0.1.0",
"status": "operational",
"modules": ["anomaly", "affiliation", "risk", "spectral", "statistics"]
}))
}
async fn scan_tender(body: web::Json<TenderScanRequest>) -> HttpResponse {
let start = std::time::Instant::now();
let prices: Vec<f64> = body.participants.iter()
.map(|p| p.bid_amount).collect();
let result = engine::anomaly::detect(
&prices,
&body.reference_prices,
);
let elapsed = start.elapsed().as_millis() as u64;
let mut flags = Vec::new();
if result.clustering_detected {
flags.push(Flag {
flag_type: "bid_clustering".into(),
severity: if result.risk_score > 0.8 { "high".into() } else { "medium".into() },
description: "Unusual price clustering detected".into(),
});
}
if result.price_anomaly {
flags.push(Flag {
flag_type: "price_anomaly".into(),
severity: "medium".into(),
description: "Deviation from reference prices".into(),
});
}
HttpResponse::Ok().json(TenderScanResult {
tender_id: body.tender_id.clone(),
risk_score: result.risk_score,
flags,
method: "svd_spectral_clustering".into(),
confidence: result.confidence,
scan_time_ms: elapsed,
})
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("╔══════════════════════════════════╗");
println!("║ BIDRADAR API v0.1.0 ║");
println!("║ Port: 8080 ║");
println!("║ Endpoints: ║");
println!("║ GET /health ║");
println!("║ POST /v1/scan/tender ║");
println!("║ POST /v1/scan/affiliation ║");
println!("║ POST /v1/predict/bankruptcy ║");
println!("║ POST /v1/analyze/timeseries ║");
println!("║ POST /v1/test/correlation ║");
println!("╚══════════════════════════════════╝");
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.route("/health", web::get().to(health))
.route("/v1/scan/tender", web::post().to(scan_tender))
})
.bind("127.0.0.1:8080")?
.run()
.await
}