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

7.3 KiB
Raw Blame History

代码审查报告

审查日期2026-07-01 审查范围software/server/src/Rust + Axumsoftware/web/src/React + TypeScript 审查工具cargo checkcargo clippytsc --noEmit、人工审计


总体评价7.5 / 10

项目整体架构清晰分层合理错误处理较为完善。Rust 端无裸 panic! / .unwrap(),前端无 as any。主要问题集中在:SQL 注入风险数据隔离缺失权限校验不完整部分文件过长


严重问题(🔴 必须修复)

问题 1操作日志查询存在 SQL 注入风险

  • 位置src/routes/operation_logs.rs:49-63
  • 描述actionstart_timeend_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.rssrc/routes/device_logs.rssrc/routes/energy_stats.rs
  • 描述:所有查询均无 WHERE organization_id = ?WHERE project_id = ? 过滤。企业管理员可以查看所有组织的数据,而非仅其管辖范围。
  • 风险:高。多租户场景下,企业 A 的管理员可以看到企业 B 的所有设备和数据。
  • 建议:在 CurrentUser 中携带 organization_id,在所有查询中自动附加 WHERE organization_id = ? 过滤。

问题 4部分路由缺少权限校验

  • 位置src/routes/charge_records.rs:158src/routes/device_logs.rs:62src/routes/energy_stats.rs:81-283src/routes/downloads.rs:87src/routes/organizations.rs:144-551
  • 描述:这些路由处理器未调用 auth::check_permission(),仅依赖 JWT 认证中间件。任何登录用户均可访问充电记录、设备日志、能耗统计、下载中心等敏感数据。
  • 风险:中。越权访问非管理类功能。
  • 建议:为每个路由添加对应的 check_permission 调用,如 user:viewenergy:view 等。

问题 5密码明文存储

  • 位置src/routes/auth.rs:51
  • 描述stored_password != input.password 直接比较明文。代码注释也明确写道"后续应改为 bcrypt/argon2 哈希"。
  • 风险:高。数据库泄露即所有用户密码暴露。
  • 建议:集成 argon2bcrypt crate在登录时 argon2::verify,在创建/重置时 argon2::hash

警告(🟡 建议修复)

警告 1organizations.rs 文件过长693 行)

  • 位置src/routes/organizations.rs
  • 描述:单个文件 693 行,包含组织/项目/柜子 CRUD + 树状结构 + 柜子详情,职责过多。
  • 建议:拆分为 organizations.rsprojects.rscabinets.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.tsxCabinetGrid.tsxAddCabinetModal.tsx 为独立组件。

警告 6Pre-existing 构建错误IconFolderOpen

  • 位置src/pages/devices/index.tsx:33
  • 描述IconFolderOpen@arco-design/web-react/icon 导入,但该版本未导出此图标。导致 vite build 失败。
  • 风险:前端无法构建生产包。
  • 建议:替换为 IconFolder 或其他可用图标。

警告 7clippy 警告 15 条

  • 位置:多处
  • 描述:主要包括 collapsible_if(可折叠嵌套 iftype_complexity(复杂类型未抽取 type aliasmanual_unwrap_or_defaultobfuscated_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