//! 组织树 API //! //! 两个独立接口: //! 1. /api/organization-tree — 返回多级组织->项目树 //! 2. /api/cabinets/filter — 根据组织/项目筛选设备列表 use axum::extract::{State, Query}; use axum::Json; use serde::Deserialize; use serde_json::{json, Value}; use super::organizations::{CabinetRow, OrganizationRow, ProjectRow, TreeNode}; use crate::error::AppError; use crate::middleware::auth::{self, CurrentUser}; use crate::commands::AppState; /// 组织树接口 — 返回多级组织->项目树(含设备数量) pub async fn organization_tree( user: CurrentUser, State(state): State, ) -> Result, AppError> { auth::check_permission(&user, "device:view")?; let db = &state.mysql; // 获取所有组织 let orgs: Vec = sqlx::query_as( "SELECT id, name, parent_id FROM organizations ORDER BY id" ) .fetch_all(db) .await?; // 获取所有项目 let projects: Vec = sqlx::query_as( "SELECT id, organization_id, name FROM projects ORDER BY id" ) .fetch_all(db) .await?; // 获取每个项目的设备数量 let project_counts: Vec<(i64, i64)> = sqlx::query_as( "SELECT project_id, COUNT(*) as cnt FROM cabinets GROUP BY project_id" ) .fetch_all(db) .await?; let project_count_map: std::collections::HashMap = project_counts.into_iter().collect(); // 递归构建树 fn build_tree( orgs: &[OrganizationRow], projects: &[ProjectRow], project_count_map: &std::collections::HashMap, parent_id: Option, ) -> Vec { orgs .iter() .filter(|o| o.parent_id == parent_id) .map(|org| { // 递归获取子组织 let children = build_tree(orgs, projects, project_count_map, Some(org.id)); // 获取该组织下的项目 let org_projects: Vec = projects .iter() .filter(|p| p.organization_id == Some(org.id)) .map(|p| { let count = project_count_map.get(&p.id).copied().unwrap_or(0); TreeNode { id: p.id, name: format!("{} ({})", p.name, count), children: None, abstract_id: None, imei: None, status: None, } }) .collect(); // 合并子组织和项目 let mut all_children = children; all_children.extend(org_projects); // 计算本组织下的总设备数量 let total_count: i64 = all_children.iter() .map(|c| { // 从项目名称中提取数量 if let Some(start) = c.name.rfind('(') { if let Some(end) = c.name.rfind(')') { c.name[start+1..end].parse().unwrap_or(0) } else { 0 } } else { 0 } }) .sum(); TreeNode { id: org.id, name: format!("{} ({})", org.name, total_count), children: if all_children.is_empty() { None } else { Some(all_children) }, abstract_id: None, imei: None, status: None, } }) .collect() } let tree = build_tree(&orgs, &projects, &project_count_map, None); Ok(Json(json!(tree))) } /// 设备列表筛选参数 #[derive(Deserialize)] pub struct CabinetFilterParams { pub organization_id: Option, pub project_id: Option, } /// 设备列表接口 — 根据组织/项目筛选 pub async fn filter_cabinets( user: CurrentUser, State(state): State, Query(params): Query, ) -> Result, AppError> { auth::check_permission(&user, "device:view")?; let db = &state.mysql; // 基础SQL:联表查询获取通道状态统计 let base_sql = " SELECT c.id, c.project_id, c.abstract_id, c.imei, c.iccid, c.name, c.address, c.status, (SELECT COUNT(*) FROM cabin_boards cb WHERE cb.cabinet_id = c.id) as board_count, (SELECT COUNT(*) FROM cabin_boards cb2 JOIN compartments comp ON comp.cabin_board_id = cb2.id WHERE cb2.cabinet_id = c.id) as channel_count, (SELECT COUNT(*) FROM cabin_boards cb3 JOIN compartments comp2 ON comp2.cabin_board_id = cb3.id WHERE cb3.cabinet_id = c.id AND comp2.status = 0) as idle_count, (SELECT COUNT(*) FROM cabin_boards cb4 JOIN compartments comp3 ON comp3.cabin_board_id = cb4.id WHERE cb4.cabinet_id = c.id AND comp3.status = 1) as charging_count, (SELECT COUNT(*) FROM cabin_boards cb5 JOIN compartments comp4 ON comp4.cabin_board_id = cb5.id WHERE cb5.cabinet_id = c.id AND comp4.status = 2) as full_count, (SELECT COUNT(*) FROM cabin_boards cb6 JOIN compartments comp5 ON comp5.cabin_board_id = cb6.id WHERE cb6.cabinet_id = c.id AND comp5.status = 3) as fault_count FROM cabinets c "; let rows = if let Some(proj_id) = params.project_id { sqlx::query_as::<_, CabinetRow>( &format!("{} WHERE c.project_id = ? ORDER BY c.id", base_sql) ) .bind(proj_id) .fetch_all(db) .await? } else if let Some(org_id) = params.organization_id { sqlx::query_as::<_, CabinetRow>( &format!("{} JOIN projects p ON c.project_id = p.id WHERE p.organization_id IN ( SELECT id FROM organizations WHERE id = ? OR parent_id = ? ) ORDER BY c.id", base_sql) ) .bind(org_id) .bind(org_id) .fetch_all(db) .await? } else if user.role_level >= 2 { sqlx::query_as::<_, CabinetRow>( &format!("{} ORDER BY c.id", base_sql) ) .fetch_all(db) .await? } else if let Some(org_id) = user.organization_id { sqlx::query_as::<_, CabinetRow>( &format!("{} JOIN projects p ON c.project_id = p.id WHERE p.organization_id = ? ORDER BY c.id", base_sql) ) .bind(org_id) .fetch_all(db) .await? } else { vec![] }; Ok(Json(json!(rows))) }