# 任务014:MIMO审查问题修复 ## 目标 修复MIMO审查发现的5个严重问题和部分警告。 ## 修复清单 ### 1. list_users元组编译问题( 高优先级) **位置:** `src/routes/users.rs` **问题:** `list_users` 使用7元素元组可能导致sqlx编译错误。 **修复方案:** ```rust // 定义结构体替代元组 #[derive(sqlx::FromRow)] struct UserRow { id: i64, phone: String, name: Option, role: i32, organization_id: Option, status: i32, created_at: chrono::NaiveDateTime, } // 查询时使用结构体 let users = sqlx::query_as::<_, UserRow>( "SELECT id, phone, name, role, organization_id, status, created_at FROM users ..." ) .fetch_all(pool) .await?; ``` ### 2. 充电记录COUNT查询缺少JOIN( 高优先级) **位置:** `src/routes/charge_records.rs` **问题:** COUNT查询缺少`compartments`表JOIN,分页总数计算错误。 **修复方案:** ```rust // 修改COUNT查询,添加JOIN let total: (i64,) = sqlx::query_as( r#"SELECT COUNT(*) FROM charge_records cr INNER JOIN compartments c ON cr.compartment_id = c.id INNER JOIN cabin_boards cb ON c.cabin_board_id = cb.id INNER JOIN cabinets cab ON cb.cabinet_id = cab.id WHERE cab.project_id = ?"# // 添加组织过滤 ) .bind(project_id) .fetch_one(pool) .await?; ``` ### 3. H5后端接口实现(🔴 高优先级) **位置:** 新建 `src/routes/h5.rs` **问题:** H5前端10+个接口均会404,后端未实现。 **修复方案:** ```rust // 新建 h5.rs 模块 use axum::extract::{Path, State}; use axum::Json; use serde_json::{json, Value}; use crate::error::AppError; use crate::middleware::auth::{self, CurrentUser}; use crate::tcp::commands::AppState; /// GET /api/h5/dashboard — H5首页数据 pub async fn get_dashboard( user: CurrentUser, State(state): State, ) -> Result, AppError> { // 查询柜子统计、今日充电、告警等 // ... } /// GET /api/h5/projects — H5项目列表 pub async fn get_projects( user: CurrentUser, State(state): State, ) -> Result, AppError> { // 查询用户有权访问的项目 // ... } /// GET /api/h5/cabinets — H5设备列表 pub async fn get_cabinets( user: CurrentUser, State(state): State, Path(project_id): Path, ) -> Result, AppError> { // 查询项目下的柜子 // ... } /// GET /api/h5/cabinets/:id — H5设备详情 pub async fn get_cabinet_detail( user: CurrentUser, State(state): State, Path(id): Path, ) -> Result, AppError> { // 查询柜子详情(仓控板+仓体) // ... } /// POST /api/h5/charge/start — H5开始充电 pub async fn start_charge( user: CurrentUser, State(state): State, Json(body): Json, ) -> Result, AppError> { auth::check_permission(&user, "charge:start")?; // 下发充电指令到设备 // ... } /// POST /api/h5/charge/stop — H5停止充电 pub async fn stop_charge( user: CurrentUser, State(state): State, Json(body): Json, ) -> Result, AppError> { auth::check_permission(&user, "charge:stop")?; // 下发停止指令到设备 // ... } /// POST /api/h5/door/open — H5开门 pub async fn open_door( user: CurrentUser, State(state): State, Json(body): Json, ) -> Result, AppError> { auth::check_permission(&user, "charge:open_door")?; // 下发开门指令到设备 // ... } ``` **路由注册:** 在 `src/routes/mod.rs` 中添加: ```rust mod h5; // 在 build 函数中添加 router = router .route("/api/h5/dashboard", get(h5::get_dashboard)) .route("/api/h5/projects", get(h5::get_projects)) .route("/api/h5/cabinets", get(h5::get_cabinets)) .route("/api/h5/cabinets/:id", get(h5::get_cabinet_detail)) .route("/api/h5/charge/start", post(h5::start_charge)) .route("/api/h5/charge/stop", post(h5::stop_charge)) .route("/api/h5/door/open", post(h5::open_door)); ``` ### 4. 文件下载端点实现(🔴 高优先级) **位置:** `src/routes/downloads.rs` **问题:** `/api/downloads/:id/file` 未实现,导出功能不完整。 **修复方案:** ```rust use axum::body::Body; use axum::http::header; use axum::response::Response; use tokio::fs; /// GET /api/downloads/:id/file — 下载文件 pub async fn download_file( user: CurrentUser, State(state): State, Path(id): Path, ) -> Result, AppError> { auth::check_permission(&user, "charge_record:export")?; let db = &state.mysql; // 查询下载任务 let task: Option = sqlx::query_as( "SELECT * FROM download_tasks WHERE id = ? AND user_id = ?" ) .bind(id) .bind(user.user_id) .fetch_optional(db) .await?; let task = task.ok_or(AppError::NotFound("下载任务不存在".into()))?; if task.status != 2 { return Err(AppError::BadRequest("文件未生成完成".into())); } // 读取文件 let file_path = task.file_path.ok_or(AppError::NotFound("文件路径不存在".into()))?; let file_content = fs::read(&file_path).await.map_err(|e| { AppError::Internal(format!("读取文件失败: {}", e)) })?; // 返回文件 let response = Response::builder() .status(200) .header(header::CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") .header(header::CONTENT_DISPOSITION, format!("attachment; filename=\"{}\"", task.file_name)) .body(Body::from(file_content)) .map_err(|e| AppError::Internal(format!("构建响应失败: {}", e)))?; Ok(response) } ``` ### 5. TCP auth_str频率限制(🟡 中优先级) **位置:** `src/tcp/handler.rs` **问题:** `auth_str`接口无频率限制,可能被暴力调用。 **修复方案:** ```rust use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; use std::time::{Duration, Instant}; // 频率限制器 pub struct RateLimiter { attempts: Arc>>>, max_attempts: usize, window: Duration, } impl RateLimiter { pub fn new(max_attempts: usize, window_secs: u64) -> Self { Self { attempts: Arc::new(RwLock::new(HashMap::new())), max_attempts, window: Duration::from_secs(window_secs), } } pub async fn check(&self, key: &str) -> bool { let mut attempts = self.attempts.write().await; let now = Instant::now(); let window_start = now - self.window; // 清理过期记录 let entry = attempts.entry(key.to_string()).or_insert_with(Vec::new); entry.retain(|t| *t > window_start); if entry.len() >= self.max_attempts { return false; // 超限 } entry.push(now); true } } // 在 handler 中使用 lazy_static::lazy_static! { static ref AUTH_STR_LIMITER: RateLimiter = RateLimiter::new(5, 300); // 5次/5分钟 } // 在 auth_str handler 中 if !AUTH_STR_LIMITER.check(&dev_id).await { return Err(AppError::TooManyRequests("请求过于频繁".into())); } ``` ## 质量约束 1. 完成代码后执行 `cargo check` + `cargo clippy` + `tsc --noEmit`,零报错零警告 2. 分层拆分,单函数≤80行,命名语义化,完整注释 3. 所有外部IO/网络请求异常捕获,禁止裸panic 4. 分支逻辑全覆盖,不遗漏兜底分支 5. 常量抽离,不使用废弃API 6. Rust内存安全,React函数组件+Hooks 7. H5接口必须添加权限校验