charging-cabinet/tasks/review-code-quality.md
2026-07-02 05:38:01 +08:00

123 lines
7.3 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.

# 代码审查报告
**审查日期**2026-07-01
**审查范围**`software/server/src/`Rust + Axum`software/web/src/`React + TypeScript
**审查工具**`cargo check``cargo clippy``tsc --noEmit`、人工审计
---
## 总体评价7.5 / 10
项目整体架构清晰分层合理错误处理较为完善。Rust 端无裸 `panic!` / `.unwrap()`,前端无 `as any`。主要问题集中在:**SQL 注入风险**、**数据隔离缺失**、**权限校验不完整**、**部分文件过长**。
---
## 严重问题(🔴 必须修复)
### 问题 1操作日志查询存在 SQL 注入风险
- **位置**`src/routes/operation_logs.rs:49-63`
- **描述**`action``start_time``end_time` 参数通过 `format!` 直接拼接到 SQL WHERE 子句中,虽然做了 `'` 替换,但仍可被绕过(如 `1; DROP TABLE users--`)。
- **风险**:攻击者可注入任意 SQL 语句,导致数据泄露或破坏。
- **建议**:改用 `sqlx::query_as` 的参数化绑定,或将条件构建逻辑统一为 `build_where_clause` 模式(如 `charge_records.rs` 已实现的那样)。
### 问题 2充电记录 LIMIT/OFFSET 通过 format! 拼接
- **位置**`src/routes/charge_records.rs:130`
- **描述**`LIMIT {} OFFSET {}` 直接使用 `format!` 拼接 `i64` 参数。虽然参数类型是 `i64`,但若未来参数类型变更或被误传字符串,将产生注入。
- **风险**:中等(当前类型安全,但模式脆弱)。
- **建议**:将 `limit` / `offset` 也通过 `.bind()` 传入。
### 问题 3无组织/项目级数据隔离
- **位置**`src/routes/organizations.rs` 全部路由、`src/routes/charge_records.rs``src/routes/device_logs.rs``src/routes/energy_stats.rs`
- **描述**:所有查询均无 `WHERE organization_id = ?``WHERE project_id = ?` 过滤。企业管理员可以查看所有组织的数据,而非仅其管辖范围。
- **风险**:高。多租户场景下,企业 A 的管理员可以看到企业 B 的所有设备和数据。
- **建议**:在 `CurrentUser` 中携带 `organization_id`,在所有查询中自动附加 `WHERE organization_id = ?` 过滤。
### 问题 4部分路由缺少权限校验
- **位置**`src/routes/charge_records.rs:158``src/routes/device_logs.rs:62``src/routes/energy_stats.rs:81-283``src/routes/downloads.rs:87``src/routes/organizations.rs:144-551`
- **描述**:这些路由处理器未调用 `auth::check_permission()`,仅依赖 JWT 认证中间件。任何登录用户均可访问充电记录、设备日志、能耗统计、下载中心等敏感数据。
- **风险**:中。越权访问非管理类功能。
- **建议**:为每个路由添加对应的 `check_permission` 调用,如 `user:view``energy:view` 等。
### 问题 5密码明文存储
- **位置**`src/routes/auth.rs:51`
- **描述**`stored_password != input.password` 直接比较明文。代码注释也明确写道"后续应改为 bcrypt/argon2 哈希"。
- **风险**:高。数据库泄露即所有用户密码暴露。
- **建议**:集成 `argon2``bcrypt` crate在登录时 `argon2::verify`,在创建/重置时 `argon2::hash`
---
## 警告(🟡 建议修复)
### 警告 1organizations.rs 文件过长693 行)
- **位置**`src/routes/organizations.rs`
- **描述**:单个文件 693 行,包含组织/项目/柜子 CRUD + 树状结构 + 柜子详情,职责过多。
- **建议**:拆分为 `organizations.rs``projects.rs``cabinets.rs` 三个文件。
### 警告 2users.rs 文件过长495 行)
- **位置**`src/routes/users.rs`
- **描述**:包含用户 CRUD、密码重置、状态管理、操作日志记录超过 80 行/函数约束(部分辅助函数虽短,但整体文件过大)。
- **建议**:将 `log_operation` 辅助函数提取到独立模块。
### 警告 3JWT 默认密钥在生产环境可能泄露
- **位置**`src/middleware/auth.rs:28`
- **描述**`JWT_SECRET_DEFAULT = "pms-dev-secret-change-me-in-production"` 如果环境变量未设置,将使用此默认值。
- **建议**:启动时若检测到默认密钥,打印 WARN 日志;或在 `config.rs` 中将 JWT_SECRET 设为必填。
### 警告 4energy_stats 导出 LIMIT/OFFSET 同样存在 format! 拼接
- **位置**`src/routes/energy_stats.rs:341`
- **描述**:与 charge_records.rs 相同的问题LIMIT/OFFSET 通过 `format!` 拼接。
- **建议**:统一使用参数化绑定。
### 警告 5devices/index.tsx 文件过长631 行)
- **位置**`src/pages/devices/index.tsx`
- **描述**:单个 TSX 文件 631 行包含组织树、柜子列表、CRUD 弹窗、右键菜单、批量添加等全部逻辑。
- **建议**:提取 `OrganizationalTree.tsx``CabinetGrid.tsx``AddCabinetModal.tsx` 为独立组件。
### 警告 6Pre-existing 构建错误IconFolderOpen
- **位置**`src/pages/devices/index.tsx:33`
- **描述**`IconFolderOpen``@arco-design/web-react/icon` 导入,但该版本未导出此图标。导致 `vite build` 失败。
- **风险**:前端无法构建生产包。
- **建议**:替换为 `IconFolder` 或其他可用图标。
### 警告 7clippy 警告 15 条
- **位置**:多处
- **描述**:主要包括 `collapsible_if`(可折叠嵌套 if`type_complexity`(复杂类型未抽取 type alias`manual_unwrap_or_default``obfuscated_if_else` 等。
- **建议**:运行 `cargo clippy --fix` 自动修复大部分问题。
### 警告 8前端 H5 页面中充电曲线为静态 Mock 数据
- **位置**`src/h5/pages/compartment-detail.tsx:156`
- **描述**:充电曲线使用硬编码的模拟数据 `[30, 45, 55, ...]`,后端未提供实时充电曲线数据。
- **建议**:待后端提供数据后替换为真实折线图。
---
## 优点
1. **Rust 错误处理规范**:无裸 `panic!` / `.unwrap()`,使用 `Result``?` 运算符,业务逻辑层错误处理完善。
2. **统一错误类型**`AppError` enum 覆盖所有错误场景,`IntoResponse` 实现统一返回格式。
3. **TCP 协议处理健壮**签名验证SHA256 + 时间窗、粘包处理LF 分隔)、连接超时清理。
4. **RBAC 权限模型**JWT + 权限码 + 角色等级(总管理员自动拥有全部权限),设计合理。
5. **前端类型安全**:无 `as any`,完整的 TypeScript 接口定义。
6. **H5 用户端实现简洁**Tailwind CSS 自定义组件,零新增依赖,扫码入口自动路由。
7. **异步导出流程完整**:任务创建 → 后台 tokio::spawn 生成 Excel → 状态更新 → 下载。
---
## 总结
| 维度 | 评分 | 说明 |
|------|------|------|
| 编译/类型检查 | 9/10 | Rust clippy 15 条警告,前端 tsc 零错误IconFolderOpen 预存问题) |
| 代码规范 | 7/10 | 部分文件过长,单函数基本在 80 行以内 |
| 错误处理 | 8/10 | Rust 端优秀,前端 try/catch 覆盖全面 |
| 安全性 | 5/10 | SQL 注入风险、明文密码、数据隔离缺失 |
| 架构设计 | 8/10 | 分层清晰路由模块化TCP 协议处理得当 |
**优先级排序**
1. 🔴 修复 SQL 注入operation_logs、energy_stats、charge_records
2. 🔴 实现密码哈希argon2/bcrypt
3. 🔴 添加组织级数据隔离
4. 🟡 补充缺失的权限校验
5. 🟡 修复 IconFolderOpen 构建错误
6. 🟡 拆分大文件organizations.rs、users.rs、devices/index.tsx