2026-07-02 05:38:01 +08:00
|
|
|
//! 组织树 API
|
|
|
|
|
//!
|
|
|
|
|
//! 返回组织->项目->设备完整树状结构。
|
|
|
|
|
//! 根据用户角色进行数据隔离:总管理员看全部,企业管理员只看本组织。
|
|
|
|
|
|
|
|
|
|
use axum::extract::State;
|
|
|
|
|
use axum::Json;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/// GET /api/organization-tree — 返回组织->项目->设备完整树
|
|
|
|
|
///
|
|
|
|
|
/// 需要 `device:view` 权限。
|
|
|
|
|
/// 数据隔离:总管理员看所有组织,企业管理员只看本组织下的数据。
|
|
|
|
|
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> = if user.role_level >= 2 {
|
|
|
|
|
sqlx::query_as(
|
|
|
|
|
r#"SELECT id, name FROM organizations ORDER BY id"#,
|
|
|
|
|
)
|
|
|
|
|
.fetch_all(db)
|
|
|
|
|
.await?
|
|
|
|
|
} else if let Some(org_id) = user.organization_id {
|
|
|
|
|
sqlx::query_as(
|
|
|
|
|
r#"SELECT id, name FROM organizations WHERE id = ? ORDER BY id"#,
|
|
|
|
|
)
|
|
|
|
|
.bind(org_id)
|
|
|
|
|
.fetch_all(db)
|
|
|
|
|
.await?
|
|
|
|
|
} else {
|
|
|
|
|
// 无组织关联的用户看不到任何数据
|
|
|
|
|
return Ok(Json(json!([])));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let projects: Vec<ProjectRow> = sqlx::query_as(
|
|
|
|
|
r#"SELECT id, organization_id, name FROM projects ORDER BY id"#,
|
|
|
|
|
)
|
|
|
|
|
.fetch_all(db)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
let cabinets: Vec<CabinetRow> = sqlx::query_as(
|
2026-07-02 21:14:43 +08:00
|
|
|
r#"SELECT id, project_id, abstract_id, imei, iccid, name, address, status
|
2026-07-02 05:38:01 +08:00
|
|
|
FROM cabinets ORDER BY id"#,
|
|
|
|
|
)
|
|
|
|
|
.fetch_all(db)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
// 构建树
|
|
|
|
|
let tree: Vec<TreeNode> = orgs
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|org| {
|
|
|
|
|
let org_projects: Vec<ProjectRow> = projects
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|p| p.organization_id == Some(org.id))
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let children: Vec<TreeNode> = org_projects
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|proj| {
|
|
|
|
|
let proj_cabinets: Vec<CabinetRow> = cabinets
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|c| c.project_id == Some(proj.id))
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let cab_nodes: Vec<TreeNode> = proj_cabinets
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|c| TreeNode {
|
|
|
|
|
id: c.id,
|
|
|
|
|
name: c.name.unwrap_or_else(|| c.abstract_id.clone()),
|
|
|
|
|
children: None,
|
|
|
|
|
abstract_id: Some(c.abstract_id),
|
|
|
|
|
imei: Some(c.imei),
|
|
|
|
|
status: Some(c.status),
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
TreeNode {
|
|
|
|
|
id: proj.id,
|
|
|
|
|
name: proj.name,
|
|
|
|
|
children: Some(cab_nodes),
|
|
|
|
|
abstract_id: None,
|
|
|
|
|
imei: None,
|
|
|
|
|
status: None,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
TreeNode {
|
|
|
|
|
id: org.id,
|
|
|
|
|
name: org.name,
|
|
|
|
|
children: Some(children),
|
|
|
|
|
abstract_id: None,
|
|
|
|
|
imei: None,
|
|
|
|
|
status: None,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(Json(json!(tree)))
|
|
|
|
|
}
|