charging-cabinet/tasks/015-nemotron-review-fixes.md
2026-07-02 05:38:01 +08:00

220 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 任务015Nemotron审查问题修复
## 目标
修复Nemotron审查发现的7个严重问题。
## 修复清单
### 1. 权限码种子数据缺失( 高优先级)
**位置:** `src/db/migrate.rs`
**问题:** `org:*``project:*``device:edit` 等权限码未在`seed_permissions`中初始化。
**修复方案:**
```rust
// 在 seed_permissions 函数中添加缺失的权限码
let permissions = vec![
// ... 已有的权限码
// 组织管理
("org:view", "查看组织"),
("org:create", "创建组织"),
("org:edit", "编辑组织"),
("org:delete", "删除组织"),
// 项目管理
("project:view", "查看项目"),
("project:create", "创建项目"),
("project:edit", "编辑项目"),
("project:delete", "删除项目"),
// 设备管理(补充)
("device:edit", "编辑设备"),
];
// 插入数据库(使用 INSERT IGNORE 避免重复)
for (code, name) in permissions {
sqlx::query(
"INSERT IGNORE INTO permissions (code, name) VALUES (?, ?)"
)
.bind(code)
.bind(name)
.execute(pool)
.await?;
}
```
**角色权限分配:**
```rust
// 总管理员 — 全部权限
// 企业管理员 — 添加 org:*, project:*, device:edit 权限
// 普通用户 — 只保留 view 权限
```
### 2. H5接口数据隔离🔴 高优先级)
**位置:** `src/routes/h5.rs`
**问题:** `get_dashboard`返回全局统计数据,`get_cabinet_detail`/`get_compartment_detail`无权限校验。
**修复方案:**
```rust
/// GET /api/h5/dashboard — H5首页数据带组织隔离
pub async fn get_dashboard(
user: CurrentUser,
State(state): State<AppState>,
) -> Result<Json<Value>, AppError> {
auth::check_permission(&user, "device:view")?;
let db = &state.mysql;
// 根据角色过滤数据
let org_filter = if user.role_level >= 2 {
None // 总管理员看全部
} else {
user.organization_id // 企业管理员只看本组织
};
// 查询柜子统计(带组织过滤)
let cabinet_stats = if let Some(org_id) = org_filter {
sqlx::query_as::<_, (i64, i64, i64, i64)>(
r#"SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as online,
SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as charging,
SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as fault
FROM cabinets c
INNER JOIN projects p ON c.project_id = p.id
WHERE p.organization_id = ?"#
)
.bind(org_id)
.fetch_one(db)
.await?
} else {
// 总管理员查询全部
// ...
};
Ok(Json(json!({
"total": cabinet_stats.0,
"online": cabinet_stats.1,
"charging": cabinet_stats.2,
"fault": cabinet_stats.3
})))
}
/// GET /api/h5/cabinets/:id — H5设备详情带权限校验
pub async fn get_cabinet_detail(
user: CurrentUser,
State(state): State<AppState>,
Path(id): Path<i64>,
) -> Result<Json<Value>, AppError> {
auth::check_permission(&user, "device:view")?;
let db = &state.mysql;
// 验证用户是否有权访问该设备
if user.role_level < 2 {
if let Some(org_id) = user.organization_id {
let access: Option<(i64,)> = sqlx::query_as(
r#"SELECT c.id FROM cabinets c
INNER JOIN projects p ON c.project_id = p.id
WHERE c.id = ? AND p.organization_id = ?"#
)
.bind(id)
.bind(org_id)
.fetch_optional(db)
.await?;
if access.is_none() {
return Err(AppError::Forbidden("无权访问该设备".into()));
}
} else {
return Err(AppError::Forbidden("无组织关联".into()));
}
}
// ... 原有查询逻辑
}
```
### 3. 前后端H5告警字段匹配🔴 中优先级)
**位置:** `src/routes/h5.rs``src/h5/pages/home.tsx`
**问题:** 后端返回`{compartment_id, cabinet_id}`,前端期望`{project, cabinet}`
**修复方案(后端):**
```rust
// 修改告警查询,返回前端期望的字段
let alerts = sqlx::query_as::<_, (String, String, String)>(
r#"SELECT
p.name as project,
cab.name as cabinet,
dl.content as message
FROM device_logs dl
INNER JOIN cabinets cab ON dl.cabinet_id = cab.id
INNER JOIN projects p ON cab.project_id = p.id
WHERE dl.log_type = 3 -- 告警
ORDER BY dl.created_at DESC
LIMIT 10"#
)
.fetch_all(db)
.await?;
Ok(Json(json!({
"alerts": alerts.iter().map(|(project, cabinet, message)| {
json!({
"project": project,
"cabinet": cabinet,
"message": message
})
}).collect::<Vec<_>>()
})))
```
### 4. 其他严重问题修复
**4.1 SQL参数化charge_records.rs**
```rust
// 确保所有SQL查询使用参数化绑定
// 检查并修复任何 format! 拼接SQL的地方
```
**4.2 数据库索引migrate.rs**
```rust
// 添加关键索引
sqlx::query("CREATE INDEX idx_cabinets_project ON cabinets(project_id)")
.execute(pool).await?;
sqlx::query("CREATE INDEX idx_charge_records_cabinet ON charge_records(cabinet_id)")
.execute(pool).await?;
sqlx::query("CREATE INDEX idx_device_logs_cabinet ON device_logs(cabinet_id)")
.execute(pool).await?;
```
**4.3 消除重复代码**
```rust
// 提取公共的组织过滤逻辑到 middleware/auth.rs
pub fn org_condition(user: &CurrentUser) -> String {
if user.role_level >= 2 {
String::new()
} else if let Some(org_id) = user.organization_id {
format!("AND organization_id = {}", org_id)
} else {
"AND 1=0".to_string()
}
}
```
## 质量约束
1. 完成代码后执行 `cargo check` + `cargo clippy` + `tsc --noEmit`,零报错零警告
2. 分层拆分单函数≤80行命名语义化完整注释
3. 所有外部IO/网络请求异常捕获禁止裸panic
4. 分支逻辑全覆盖,不遗漏兜底分支
5. 常量抽离不使用废弃API
6. Rust内存安全React函数组件+Hooks
7. H5接口必须添加权限校验和组织数据隔离