feat(boc): v1.0 - Complete Business Operations Center
- Go backend API with full CRUD for all modules - Rust analytics service with parallel processing - C runtime with POSIX shared memory IPC - PostgreSQL schema with 30+ tables - Redis cache, Kafka event streaming - WebSocket hub, automation engine - PDF generation, Resend email integration - JWT auth, multi-tenant - Docker Compose deployment - Nginx reverse proxy Refs: BOC-001
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
Json,
|
||||
extract::State,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{info, error};
|
||||
|
||||
mod analytics;
|
||||
mod reports;
|
||||
mod ipc;
|
||||
|
||||
use analytics::AnalyticsEngine;
|
||||
use reports::ReportGenerator;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
analytics: Arc<RwLock<AnalyticsEngine>>,
|
||||
reports: Arc<RwLock<ReportGenerator>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct HealthResponse {
|
||||
status: String,
|
||||
service: String,
|
||||
version: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ReportRequest {
|
||||
tenant_id: String,
|
||||
report_type: String,
|
||||
parameters: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ReportResponse {
|
||||
report_id: String,
|
||||
status: String,
|
||||
data: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AnalyticsRequest {
|
||||
tenant_id: String,
|
||||
metric: String,
|
||||
period: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AnalyticsResponse {
|
||||
metric: String,
|
||||
value: f64,
|
||||
trend: f64,
|
||||
breakdown: Vec<BreakdownItem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BreakdownItem {
|
||||
label: String,
|
||||
value: f64,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter("boc_rust_service=info")
|
||||
.init();
|
||||
|
||||
info!("BOC Rust Service starting...");
|
||||
|
||||
let state = AppState {
|
||||
analytics: Arc::new(RwLock::new(AnalyticsEngine::new())),
|
||||
reports: Arc::new(RwLock::new(ReportGenerator::new())),
|
||||
};
|
||||
|
||||
let app = Router::new()
|
||||
.route("/health", get(health_handler))
|
||||
.route("/api/v1/reports/generate", post(generate_report))
|
||||
.route("/api/v1/analytics/query", post(query_analytics))
|
||||
.route("/api/v1/analytics/batch", post(batch_analytics))
|
||||
.with_state(state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:9093")
|
||||
.await
|
||||
.expect("Failed to bind port 9093");
|
||||
|
||||
info!("BOC Rust Service listening on 0.0.0.0:9093");
|
||||
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.expect("Server failed");
|
||||
}
|
||||
|
||||
async fn health_handler() -> Json<HealthResponse> {
|
||||
Json(HealthResponse {
|
||||
status: "ok".to_string(),
|
||||
service: "boc-rust-service".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn generate_report(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<ReportRequest>,
|
||||
) -> Json<ReportResponse> {
|
||||
info!("Generating report: {} for tenant: {}", req.report_type, req.tenant_id);
|
||||
|
||||
let reports = state.reports.read().await;
|
||||
match reports.generate(&req.tenant_id, &req.report_type, &req.parameters).await {
|
||||
Ok(data) => Json(ReportResponse {
|
||||
report_id: uuid::Uuid::new_v4().to_string(),
|
||||
status: "completed".to_string(),
|
||||
data: Some(data),
|
||||
}),
|
||||
Err(e) => {
|
||||
error!("Report generation failed: {}", e);
|
||||
Json(ReportResponse {
|
||||
report_id: uuid::Uuid::new_v4().to_string(),
|
||||
status: "failed".to_string(),
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn query_analytics(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<AnalyticsRequest>,
|
||||
) -> Json<AnalyticsResponse> {
|
||||
let analytics = state.analytics.read().await;
|
||||
let result = analytics.query(&req.tenant_id, &req.metric, &req.period).await;
|
||||
|
||||
Json(AnalyticsResponse {
|
||||
metric: req.metric,
|
||||
value: result.value,
|
||||
trend: result.trend,
|
||||
breakdown: result.breakdown.into_iter()
|
||||
.map(|(label, value)| BreakdownItem { label, value })
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn batch_analytics(
|
||||
State(state): State<AppState>,
|
||||
Json(reqs): Json<Vec<AnalyticsRequest>>,
|
||||
) -> Json<Vec<AnalyticsResponse>> {
|
||||
let analytics = state.analytics.read().await;
|
||||
|
||||
let mut responses = Vec::with_capacity(reqs.len());
|
||||
for req in reqs {
|
||||
let result = analytics.query(&req.tenant_id, &req.metric, &req.period).await;
|
||||
responses.push(AnalyticsResponse {
|
||||
metric: req.metric.clone(),
|
||||
value: result.value,
|
||||
trend: result.trend,
|
||||
breakdown: result.breakdown.into_iter()
|
||||
.map(|(label, value)| BreakdownItem { label, value })
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
|
||||
Json(responses)
|
||||
}
|
||||
Reference in New Issue
Block a user