158 lines
4.4 KiB
Rust
Raw Normal View History

//! 设备日志 API — 分页列表查询
use axum::extract::{Query, State};
use axum::Json;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::error::AppError;
use crate::middleware::auth::{self, CurrentUser};
/// 设备日志查询参数
#[derive(Debug, Deserialize)]
pub struct DeviceLogQuery {
pub cabinet_id: Option<i64>,
pub log_type: Option<i8>,
pub start_time: Option<String>,
pub end_time: Option<String>,
#[serde(default = "default_page")]
pub page: i64,
#[serde(default = "default_page_size")]
pub page_size: i64,
}
/// 设备日志列表项
#[derive(Debug, Serialize)]
pub struct DeviceLogItem {
pub id: i64,
pub cabinet_id: i64,
pub cabinet_name: Option<String>,
pub log_type: i8,
pub log_type_text: String,
pub content: Option<String>,
pub created_at: Option<String>,
}
fn default_page() -> i64 { 1 }
fn default_page_size() -> i64 { 20 }
/// 日志类型码转中文
fn log_type_text(log_type: i8) -> &'static str {
match log_type {
1 => "状态上报",
2 => "故障",
3 => "告警",
4 => "停电",
_ => "未知",
}
}
/// 数据库行映射
#[derive(Debug, sqlx::FromRow)]
struct DeviceLogRow {
id: i64,
cabinet_id: i64,
cabinet_name: Option<String>,
log_type: i8,
content: Option<String>,
created_at: Option<NaiveDateTime>,
}
/// GET /api/device-logs — 设备日志分页列表
pub async fn list_device_logs(
user: CurrentUser,
State(state): State<super::AppState>,
Query(params): Query<DeviceLogQuery>,
) -> Result<Json<Value>, AppError> {
auth::check_permission(&user, "device_log:view")?;
let offset = (params.page - 1) * params.page_size;
// 构建 WHERE 条件
let mut conditions = Vec::new();
let mut bind_values: Vec<String> = Vec::new();
if let Some(cabinet_id) = params.cabinet_id {
conditions.push("dl.cabinet_id = ?");
bind_values.push(cabinet_id.to_string());
}
if let Some(log_type) = params.log_type {
conditions.push("dl.log_type = ?");
bind_values.push(log_type.to_string());
}
if let Some(ref start_time) = params.start_time {
conditions.push("dl.created_at >= ?");
bind_values.push(start_time.clone());
}
if let Some(ref end_time) = params.end_time {
conditions.push("dl.created_at <= ?");
bind_values.push(end_time.clone());
}
// 追加组织数据隔离
let (org_cond, org_binds) = auth::org_condition(&user, "dl");
if !org_cond.is_empty() {
conditions.push(org_cond.trim_start_matches(" AND "));
bind_values.extend(org_binds);
}
let where_clause = if conditions.is_empty() {
String::new()
} else {
format!("WHERE {}", conditions.join(" AND "))
};
// 查询总数
let count_sql = format!(
"SELECT COUNT(*) FROM device_logs dl {}",
where_clause
);
let mut count_query = sqlx::query_scalar::<_, i64>(&count_sql);
for val in &bind_values {
count_query = count_query.bind(val);
}
let total = count_query.fetch_one(&state.mysql).await?;
// 查询列表
let list_sql = format!(
"SELECT dl.id, dl.cabinet_id, c.name AS cabinet_name, \
dl.log_type, dl.content, dl.created_at \
FROM device_logs dl \
LEFT JOIN cabinets c ON dl.cabinet_id = c.id \
{} ORDER BY dl.created_at DESC LIMIT ? OFFSET ?",
where_clause
);
let mut list_query = sqlx::query_as::<_, DeviceLogRow>(&list_sql);
for val in &bind_values {
list_query = list_query.bind(val);
}
list_query = list_query.bind(params.page_size).bind(offset);
let rows = list_query.fetch_all(&state.mysql).await?;
let list: Vec<DeviceLogItem> = rows
.into_iter()
.map(|r| DeviceLogItem {
id: r.id,
cabinet_id: r.cabinet_id,
cabinet_name: r.cabinet_name,
log_type: r.log_type,
log_type_text: log_type_text(r.log_type).to_string(),
content: r.content,
created_at: r.created_at.map(|t| t.to_string()),
})
.collect();
Ok(Json(json!({
"code": 0,
"data": {
"list": list,
"total": total,
"page": params.page,
"page_size": params.page_size,
},
"message": "ok"
})))
}