190 lines
6.6 KiB
Rust
Raw Normal View History

//! 组织树 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<AppState>,
) -> Result<Json<Value>, AppError> {
auth::check_permission(&user, "device:view")?;
let db = &state.mysql;
// 获取所有组织
let orgs: Vec<OrganizationRow> = sqlx::query_as(
"SELECT id, name, parent_id FROM organizations ORDER BY id"
)
.fetch_all(db)
.await?;
// 获取所有项目
let projects: Vec<ProjectRow> = 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<i64, i64> = project_counts.into_iter().collect();
// 递归构建树
fn build_tree(
orgs: &[OrganizationRow],
projects: &[ProjectRow],
project_count_map: &std::collections::HashMap<i64, i64>,
parent_id: Option<i64>,
) -> Vec<TreeNode> {
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<TreeNode> = 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<i64>,
pub project_id: Option<i64>,
}
/// 设备列表接口 — 根据组织/项目筛选
pub async fn filter_cabinets(
user: CurrentUser,
State(state): State<AppState>,
Query(params): Query<CabinetFilterParams>,
) -> Result<Json<Value>, 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)))
}