diff --git a/.gitignore b/.gitignore
index 73df920..f5d5f94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,6 @@ Thumbs.db
# Temporary
*.tmp
*.log
+
+# Tasks
+tasks/
diff --git a/tasks/001-server-skeleton.md b/tasks/001-server-skeleton.md
deleted file mode 100644
index b06fe62..0000000
--- a/tasks/001-server-skeleton.md
+++ /dev/null
@@ -1,221 +0,0 @@
-# 任务001:后端骨架
-
-## 目标
-
-搭建Rust + Axum后端项目骨架,连接MySQL和Redis,建立基础路由框架。
-
-## 技术栈
-
-- Rust + Axum + Tokio
-- MySQL (sqlx)
-- Redis (redis crate)
-
-## 数据库连接
-
-```
-MySQL: 10.8.0.252:3306 (root / Hbhyg731024@)
-Redis: 10.8.0.252:6379 (password: Hbhyg731024@, db: 5)
-```
-
-**注意:** 不要动现有的 `AZZCWeChat*` 数据库,新建 `pms_dev` 库。
-
-## 项目结构
-
-```
-software/server/
-├── Cargo.toml
-├── src/
-│ ├── main.rs # 启动入口
-│ ├── config.rs # 配置管理
-│ ├── db/
-│ │ ├── mod.rs # MySQL连接池
-│ │ └── migrate.rs # 数据库迁移(建表)
-│ ├── redis.rs # Redis连接
-│ ├── routes/
-│ │ ├── mod.rs # 路由注册
-│ │ └── health.rs # 健康检查接口
-│ ── error.rs # 统一错误处理
-```
-
-## 数据库表
-
-创建 `pms_dev` 库,执行以下建表SQL:
-
-```sql
--- 组织表
-CREATE TABLE organizations (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- name VARCHAR(100) NOT NULL COMMENT '组织名称',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 项目表
-CREATE TABLE projects (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- organization_id BIGINT NOT NULL,
- name VARCHAR(100) NOT NULL COMMENT '项目名称',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 充电柜表
-CREATE TABLE cabinets (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- project_id BIGINT,
- abstract_id VARCHAR(20) NOT NULL UNIQUE COMMENT '平台抽象ID,如10-00000000',
- imei VARCHAR(15) NOT NULL UNIQUE COMMENT '4G模块IMEI',
- auth_str VARCHAR(8) COMMENT '签名安全码',
- name VARCHAR(100) COMMENT '柜子名称',
- status TINYINT DEFAULT 0 COMMENT '0离线 1在线',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- FOREIGN KEY (project_id) REFERENCES projects(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 仓控板表
-CREATE TABLE cabin_boards (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- cabinet_id BIGINT NOT NULL,
- board_id TINYINT NOT NULL COMMENT '仓控板ID 1-6',
- status TINYINT DEFAULT 0,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- FOREIGN KEY (cabinet_id) REFERENCES cabinets(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 仓体表
-CREATE TABLE compartments (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- cabin_board_id BIGINT NOT NULL,
- channel_id TINYINT NOT NULL COMMENT '通道ID 1-6',
- status TINYINT DEFAULT 0 COMMENT '0空闲 1充电中 2故障 3禁用',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- FOREIGN KEY (cabin_board_id) REFERENCES cabin_boards(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 用户表
-CREATE TABLE users (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- phone VARCHAR(11) NOT NULL UNIQUE COMMENT '手机号',
- name VARCHAR(50) COMMENT '姓名',
- password VARCHAR(100) NOT NULL,
- role TINYINT DEFAULT 0 COMMENT '0普通 1企业管理员 2总管理员',
- organization_id BIGINT,
- status TINYINT DEFAULT 1 COMMENT '0禁用 1启用',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 充电记录表
-CREATE TABLE charge_records (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- compartment_id BIGINT NOT NULL,
- cabinet_id BIGINT NOT NULL,
- start_time TIMESTAMP,
- end_time TIMESTAMP,
- energy DECIMAL(10,2) COMMENT '充电量kWh',
- max_temperature DECIMAL(5,1) COMMENT '最高温度',
- status TINYINT DEFAULT 0 COMMENT '0正常 1异常 2手动停止',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (compartment_id) REFERENCES compartments(id),
- FOREIGN KEY (cabinet_id) REFERENCES cabinets(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 设备日志表
-CREATE TABLE device_logs (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- cabinet_id BIGINT NOT NULL,
- log_type TINYINT NOT NULL COMMENT '1状态上报 2故障 3告警 4停电',
- content TEXT COMMENT '日志内容JSON',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (cabinet_id) REFERENCES cabinets(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 能耗统计表
-CREATE TABLE energy_stats (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- cabinet_id BIGINT NOT NULL,
- stat_date DATE NOT NULL,
- total_energy DECIMAL(10,2) COMMENT '总能耗kWh',
- charge_count INT DEFAULT 0 COMMENT '充电次数',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (cabinet_id) REFERENCES cabinets(id),
- UNIQUE KEY uk_cabinet_date (cabinet_id, stat_date)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 操作日志表
-CREATE TABLE operation_logs (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- user_id BIGINT NOT NULL,
- action VARCHAR(100) NOT NULL COMMENT '操作动作',
- target_type VARCHAR(50) COMMENT '操作对象类型',
- target_id BIGINT COMMENT '操作对象ID',
- detail TEXT COMMENT '操作详情',
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (user_id) REFERENCES users(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-```
-
-## API路由
-
-注册以下基础路由:
-
-```rust
-// 健康检查
-GET /api/health → 返回 {"status": "ok"}
-
-// 认证
-POST /api/auth/login → 登录
-POST /api/auth/logout → 登出
-
-// 组织管理
-GET /api/organizations → 组织列表
-POST /api/organizations → 创建组织
-PUT /api/organizations/:id → 更新组织
-DELETE /api/organizations/:id → 删除组织
-
-// 项目管理
-GET /api/projects → 项目列表
-POST /api/projects → 创建项目
-PUT /api/projects/:id → 更新项目
-DELETE /api/projects/:id → 删除项目
-
-// 设备管理
-GET /api/cabinets → 柜子列表
-POST /api/cabinets → 添加柜子(支持批量IMEI)
-PUT /api/cabinets/:id → 更新柜子
-DELETE /api/cabinets/:id → 删除柜子
-POST /api/cabinets/:id/regenerate-auth → 重新生成auth_str
-
-// 充电记录
-GET /api/charge-records → 充电记录列表
-GET /api/charge-records/export → 导出充电记录
-
-// 设备日志
-GET /api/device-logs → 设备日志列表
-
-// 能耗管理
-GET /api/energy-stats → 能耗统计
-
-// 用户管理
-GET /api/users → 用户列表
-POST /api/users → 创建用户
-PUT /api/users/:id → 更新用户
-PUT /api/users/:id/reset-password → 重置密码
-
-// 操作日志
-GET /api/operation-logs → 操作日志列表
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check`,必须零报错、零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
diff --git a/tasks/002-web-scaffold.md b/tasks/002-web-scaffold.md
deleted file mode 100644
index 7df8167..0000000
--- a/tasks/002-web-scaffold.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# 任务002:前端骨架
-
-## 目标
-
-搭建React + Arco Design Pro前端项目,配置菜单和路由,实现登录页。
-
-## 技术栈
-
-- React + TypeScript
-- Arco Design Pro (React版)
-- Vite
-- Tailwind CSS
-
-## 项目结构
-
-```
-software/web/
-├── package.json
-├── vite.config.ts
-├── src/
-│ ├── App.tsx
-│ ├── main.tsx
-│ ├── layouts/
-│ │ └── AdminLayout.tsx # 后台布局(侧栏+顶栏+内容区)
-│ ├── pages/
-│ │ ├── Login.tsx # 登录页
-│ │ ├── Dashboard.tsx # 首页概览
-│ │ ├── devices/ # 设备管理
-│ │ ├── charge-records/ # 充电记录
-│ │ ├── device-logs/ # 设备日志
-│ │ ├── energy/ # 能耗管理
-│ │ └── settings/ # 系统设置
-│ ├── api/
-│ │ └── request.ts # HTTP请求封装
-│ ├── stores/
-│ │ └── auth.ts # 认证状态
-│ └── utils/
-│ └── permission.ts # 权限工具
-```
-
-## 菜单配置
-
-```typescript
-const menuConfig = [
- { key: 'dashboard', label: 'Dashboard', icon: 'Dashboard' },
- { key: 'devices', label: '设备管理', icon: 'Device' },
- { key: 'charge-records', label: '充电记录', icon: 'File' },
- { key: 'device-logs', label: '设备日志', icon: 'Bug' },
- { key: 'energy', label: '能耗管理', icon: 'Thunderbolt' },
- { key: 'settings', label: '系统设置', icon: 'Settings', children: [
- { key: 'users', label: '用户管理' },
- { key: 'roles', label: '角色权限' },
- { key: 'operation-logs', label: '操作日志' }
- ]}
-];
-```
-
-## 登录页
-
-- 手机号 + 密码登录
-- 登录后存储token到localStorage
-- 跳转到Dashboard
-
-## API基础配置
-
-```typescript
-// 开发环境
-const API_BASE = 'http://localhost:3000/api';
-
-// 请求封装
-const request = async (url, options) => {
- const token = localStorage.getItem('token');
- const res = await fetch(`${API_BASE}${url}`, {
- ...options,
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${token}`
- }
- });
- if (!res.ok) throw new Error(res.statusText);
- return res.json();
-};
-```
-
-## 质量约束
-
-1. 完成代码后执行 `tsc --noEmit`,必须零报错、零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. React函数式组件+Hooks,不写Class组件
diff --git a/tasks/003-tcp-server.md b/tasks/003-tcp-server.md
deleted file mode 100644
index 4257058..0000000
--- a/tasks/003-tcp-server.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# 任务003:TCP通讯服务
-
-## 目标
-
-实现TCP长连接服务,处理设备登录、签名验证、数据上报、指令下发。
-
-## 技术栈
-
-- Rust + Tokio
-- TCP长连接
-
-## 协议规范
-
-### 通信规则
-- 长连接,无需PING包
-- 空闲1分钟上报,充电15秒上报
-- 超时5000ms
-- JSON去空格回车省流量,LF处理粘包
-- `dev_id`:IMEI(10进制)
-- `sub_device_id`:485 ID
-- `msg_id`:自增会话ID
-
-### 签名机制
-- 设备首次连接调 `auth_str` 接口获取8位安全码(仅一次)
-- `sign = SHA256(dev_id + auth_str + timestamp).substring(56, 63)`
-- 时间戳秒级,平台校验±60秒
-
-### 接口清单
-
-| 动作 | 方向 | 说明 |
-|------|------|------|
-| `login` | 设备→平台 | 登录(需sign签名+timestamp) |
-| `auth_str` | 设备→平台 | 获取安全码(仅首次) |
-| `off` | 双向 | 停止供电 |
-| `status_post` | 设备→平台 | 定时状态上报(rssi/status) |
-| `on` | 平台→设备 | 开始供电 |
-| `open` | 平台→设备 | 开门(错误码63=门锁故障) |
-| `status_get` | 平台→设备 | 读取仓板状态 |
-| `reset` | 平台→设备 | 设备重启 |
-| `pow_fail` | 设备→平台 | 停电上报 |
-
-## 实现要求
-
-### TCP Server
-- 监听端口:3002(可配置)
-- 连接管理:维护设备连接池
-- 心跳检测:5000ms超时断开
-- 粘包处理:LF分隔
-
-### 登录流程
-1. 设备发送 `{"act":"login","dev_id":"IMEI","msg_id":1,"timestamp":xxx,"sign":"xxx"}`
-2. 平台验证签名
-3. 验证通过 → 返回 `{"suc":1,"msg_id":1}`
-4. 验证失败 → 返回 `{"suc":0,"err":"签名错误"}`
-
-### 数据上报处理
-- `status_post`:解析status HEX数据,存入Redis(实时)+ MySQL(持久化)
-- `pow_fail`:记录停电日志
-
-### 指令下发
-- 提供API接口供HTTP服务调用
-- 通过设备连接池下发指令
-- 设备不在线时返回错误
-
-## 项目结构
-
-```
-software/server/src/
-├── tcp/
-│ ├── mod.rs # TCP服务入口
-│ ├── server.rs # TCP Server实现
-│ ├── connection.rs # 连接管理
-│ ├── protocol.rs # 协议解析
-│ ├── handler.rs # 消息处理
-│ └── commands.rs # 指令下发
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check`,必须零报错、零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
diff --git a/tasks/004-organization-management.md b/tasks/004-organization-management.md
deleted file mode 100644
index d44d039..0000000
--- a/tasks/004-organization-management.md
+++ /dev/null
@@ -1,119 +0,0 @@
-# 任务004:组织架构管理
-
-## 目标
-
-实现组织、项目、设备的增删改查,树状菜单展示,设备划拨功能。
-
-## 后端API
-
-### 组织管理
-```
-GET /api/organizations # 组织列表
-POST /api/organizations # 创建组织
-PUT /api/organizations/:id # 更新组织
-DELETE /api/organizations/:id # 删除组织
-```
-
-### 项目管理
-```
-GET /api/organizations/:org_id/projects # 项目列表(按组织)
-POST /api/organizations/:org_id/projects # 创建项目
-PUT /api/projects/:id # 更新项目
-DELETE /api/projects/:id # 删除项目
-```
-
-### 设备管理
-```
-GET /api/projects/:project_id/cabinets # 柜子列表(按项目)
-POST /api/cabinets # 添加柜子(支持批量IMEI)
-PUT /api/cabinets/:id # 更新柜子(划拨项目)
-DELETE /api/cabinets/:id # 删除柜子
-POST /api/cabinets/:id/regenerate-auth # 重新生成auth_str
-```
-
-### 树状结构
-```
-GET /api/organization-tree # 返回完整树:组织→项目→设备
-```
-
-返回格式:
-```json
-{
- "id": 1,
- "name": "组织A",
- "children": [
- {
- "id": 1,
- "name": "项目1",
- "children": [
- {"id": 1, "abstract_id": "10-00000001", "imei": "123456789012345", "status": 1}
- ]
- }
- ]
-}
-```
-
-## 前端页面
-
-### 设备管理页
-
-**布局:**
-```
-┌─────────────────┬──────────────────────────────────
-│ 组织架构树 │ 卡片网格 │
-│ ├─ 全部 │ ┌─────┐ ┌─────┐ ┌─────┐ │
-│ ├─ 组织A │ │柜子1 │ │柜子2 │ │柜子3 │ │
-│ │ ├─ 项目1 │ │ID │ │ID │ │ID │ │
-│ │ └─ 项目2 │ │状态 │ │状态 │ │状态 │ │
-│ ─ 组织B │ └─────┘ └─────┘ └─────┘ │
-│ │ │
-└─────────────────┴──────────────────────────────────┘
-```
-
-**左侧树:**
-- 显示组织→项目→设备树状结构
-- 右键菜单:添加组织、添加项目、编辑、删除
-- 点击节点过滤右侧卡片
-
-**右侧卡片:**
-- 显示柜子ID、状态(在线/离线/充电中/故障)
-- 点击卡片进入柜子详情页
-
-### 柜子详情页
-
-**布局:**
-```
-┌──────────────────────────────────────────────┐
-│ ← 返回 柜子10-00000000 状态: 在线 │
-├──────────────────────────────────────────────┤
-│ 仓板1 (ID:1) │
-│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐│
-│ │通道1 │ │通道2 │ │通道3 │ │通道4 │ │通道5 │ │通道6 ││
-│ │状态 │ │状态 │ │状态 │ │状态 │ │状态 │ │状态 ││
-│ │电压 │ │电压 │ │电压 │ │电压 │ │电压 │ │电压 ││
-│ │电流 │ │电流 │ │电流 │ │电流 │ │电流 │ │电流 ││
-│ │功率 │ │功率 │ │功率 │ │功率 │ │功率 │ │功率 ││
-│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘│
-├──────────────────────────────────────────────┤
-│ 仓板2 (ID:2) ... │
-└──────────────────────────────────────────────
-```
-
-**通道卡片显示:** 工作状态、电压、电流、功率
-
-**点击通道 → 模态框显示充电记录**
-
-### 添加柜子弹窗
-
-- 文本框输入IMEI,一行一个(支持批量)
-- 选择所属组织/项目
-- 提交后后端生成auth_str
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
diff --git a/tasks/005-permission-system.md b/tasks/005-permission-system.md
deleted file mode 100644
index 74fc11a..0000000
--- a/tasks/005-permission-system.md
+++ /dev/null
@@ -1,171 +0,0 @@
-# 任务005:权限系统
-
-## 目标
-
-实现RBAC权限控制,精确到按钮级别。
-
-## 权限模型
-
-### 角色
-- **总管理员** — 全部权限,管理所有组织/项目/设备
-- **企业管理员** — 只能管理自己组织下的项目/设备/人员
-- **普通用户** — 根据配置查看/操作特定组织/项目/设备
-
-### 权限粒度
-- **组织级** — 用户可看/操作整个组织下所有设备
-- **项目级** — 用户只能看/操作某项目下的设备
-- **设备级** — 用户只能看/操作某一台柜子
-
-### 权限类型
-- `view` — 查看
-- `operate` — 操作(控制设备)
-
-## 数据库表
-
-```sql
--- 角色表
-CREATE TABLE roles (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- name VARCHAR(50) NOT NULL UNIQUE,
- description VARCHAR(200),
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 权限表
-CREATE TABLE permissions (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- code VARCHAR(50) NOT NULL UNIQUE COMMENT '权限码,如device:view',
- name VARCHAR(100) NOT NULL,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 角色权限关联表
-CREATE TABLE role_permissions (
- role_id BIGINT NOT NULL,
- permission_id BIGINT NOT NULL,
- PRIMARY KEY (role_id, permission_id),
- FOREIGN KEY (role_id) REFERENCES roles(id),
- FOREIGN KEY (permission_id) REFERENCES permissions(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 用户角色关联表
-CREATE TABLE user_roles (
- user_id BIGINT NOT NULL,
- role_id BIGINT NOT NULL,
- PRIMARY KEY (user_id, role_id),
- FOREIGN KEY (user_id) REFERENCES users(id),
- FOREIGN KEY (role_id) REFERENCES roles(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
--- 用户权限范围表(组织/项目/设备级)
-CREATE TABLE user_scopes (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- user_id BIGINT NOT NULL,
- scope_type TINYINT NOT NULL COMMENT '1组织 2项目 3设备',
- scope_id BIGINT NOT NULL,
- permission_type TINYINT NOT NULL COMMENT '1查看 2操作',
- FOREIGN KEY (user_id) REFERENCES users(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-```
-
-## 后端API
-
-### 角色管理
-```
-GET /api/roles # 角色列表
-POST /api/roles # 创建角色
-PUT /api/roles/:id # 更新角色
-DELETE /api/roles/:id # 删除角色
-PUT /api/roles/:id/permissions # 设置角色权限
-```
-
-### 用户权限
-```
-GET /api/users/:id/permissions # 获取用户权限
-PUT /api/users/:id/roles # 设置用户角色
-PUT /api/users/:id/scopes # 设置用户权限范围
-```
-
-### 权限校验中间件
-```rust
-// 路由级别权限校验
-#[middleware]
-fn require_permission(permission: &str) {
- // 1. 从token获取用户ID
- // 2. 查询用户角色和权限
- // 3. 校验权限范围(组织/项目/设备)
- // 4. 无权限返回403
-}
-```
-
-## 前端组件
-
-### 权限组件
-```tsx
-// 按钮级权限控制
-
-
-
-
-// 页面级权限控制
-
-
-
-```
-
-### 角色管理页
-- 角色列表
-- 创建/编辑角色
-- 权限树勾选(按模块分组)
-
-### 用户权限配置
-- 选择角色
-- 配置权限范围(组织/项目/设备)
-- 选择权限类型(查看/操作)
-
-## 权限码列表
-
-```
-# 设备管理
-device:view # 查看设备
-device:operate # 操作设备
-device:create # 添加设备
-device:delete # 删除设备
-
-# 充电控制
-charge:start # 开始充电
-charge:stop # 停止充电
-charge:open_door # 开门
-
-# 充电记录
-charge_record:view # 查看充电记录
-charge_record:export # 导出充电记录
-
-# 设备日志
-device_log:view # 查看设备日志
-
-# 能耗管理
-energy:view # 查看能耗
-energy:export # 导出能耗
-
-# 系统设置
-user:view # 查看用户
-user:create # 创建用户
-user:edit # 编辑用户
-user:delete # 删除用户
-role:view # 查看角色
-role:create # 创建角色
-role:edit # 编辑角色
-role:delete # 删除角色
-operation_log:view # 查看操作日志
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
-7. 权限校验必须覆盖所有API接口
diff --git a/tasks/006-charge-records-and-logs.md b/tasks/006-charge-records-and-logs.md
deleted file mode 100644
index 044cc50..0000000
--- a/tasks/006-charge-records-and-logs.md
+++ /dev/null
@@ -1,113 +0,0 @@
-# 任务006:充电记录与设备日志
-
-## 目标
-
-实现充电记录查询/导出、设备日志查看、异步导出下载中心。
-
-## 后端API
-
-### 充电记录
-```
-GET /api/charge-records # 充电记录列表
- ?cabinet_id=xxx&cabin_board_id=xxx&compartment_id=xxx
- &start_time=xxx&end_time=xxx&page=1&page_size=20
-
-GET /api/charge-records/export # 导出充电记录(异步)
- ?cabinet_id=xxx&start_time=xxx&end_time=xxx
-```
-
-### 设备日志
-```
-GET /api/device-logs # 设备日志列表
- ?cabinet_id=xxx&log_type=xxx
- &start_time=xxx&end_time=xxx&page=1&page_size=20
-```
-
-### 下载中心
-```
-GET /api/downloads # 下载任务列表
-GET /api/downloads/:id # 获取下载链接
-POST /api/downloads/cleanup # 清理过期文件
-```
-
-## 异步导出流程
-
-```
-1. 用户点击"导出" → POST /api/charge-records/export
-2. 后端创建下载任务,返回任务ID
-3. 后台异步生成Excel文件
-4. 用户到"下载中心"查看进度
-5. 完成后点击下载
-```
-
-### 下载任务表
-```sql
-CREATE TABLE download_tasks (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- user_id BIGINT NOT NULL,
- task_type VARCHAR(50) NOT NULL COMMENT 'charge_records/energy_stats',
- status TINYINT DEFAULT 0 COMMENT '0排队 1处理中 2完成 3失败',
- file_path VARCHAR(500) COMMENT '文件路径',
- file_name VARCHAR(200) COMMENT '文件名',
- progress INT DEFAULT 0 COMMENT '进度百分比',
- error_message TEXT,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- completed_at TIMESTAMP,
- FOREIGN KEY (user_id) REFERENCES users(id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-```
-
-### 文件名智能生成
-```
-充电记录_项目A_20260701-20260731.xlsx
-能耗统计_组织B_2026年7月.xlsx
-设备日志_柜子10-00000001_20260701.xlsx
-```
-
-## 前端页面
-
-### 充电记录页
-
-**顶部过滤:**
-- 柜子下拉(可选)
-- 仓板下拉(可选,依赖柜子)
-- 通道下拉(可选,依赖仓板)
-- 时间范围选择器
-- 查询按钮 + 导出按钮
-
-**列表显示:**
-| 柜子ID | 仓板 | 通道 | 开始时间 | 结束时间 | 充电量 | 最高温度 | 状态 |
-|--------|------|------|----------|----------|--------|----------|------|
-
-**分页:** 每页20条
-
-### 设备日志页
-
-**顶部过滤:**
-- 柜子下拉
-- 日志类型(状态上报/故障/告警/停电)
-- 时间范围
-
-**列表显示:**
-| 柜子ID | 类型 | 内容摘要 | 时间 |
-|--------|------|----------|------|
-
-### 下载中心
-
-**位置:** 充电记录页/能耗管理页的"导出"按钮旁
-
-**弹窗显示:**
-| 任务名称 | 状态 | 进度 | 操作 |
-|----------|------|------|------|
-| 充电记录_项目A... | 处理中 | 65% | 等待... |
-| 能耗统计_组织B... | 完成 | 100% | [下载] |
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
-7. 导出任务异步执行,不阻塞请求
diff --git a/tasks/007-energy-management.md b/tasks/007-energy-management.md
deleted file mode 100644
index aae9ff6..0000000
--- a/tasks/007-energy-management.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# 任务007:能耗管理
-
-## 目标
-
-实现电表数据统计,柜子/项目维度的能耗分析,趋势图表。
-
-## 后端API
-
-```
-GET /api/energy-stats/summary # 总览(今日/本月总能耗、电费估算)
-GET /api/energy-stats/cabinets # 柜子能耗排行
-GET /api/energy-stats/projects # 项目能耗统计
-GET /api/energy-stats/trend # 趋势数据(日/周/月)
-GET /api/energy-stats/export # 导出能耗报表(异步)
-```
-
-### 响应示例
-
-**总览:**
-```json
-{
- "today_energy": 3750.50,
- "month_energy": 85200.00,
- "estimated_cost": 68160.00,
- "cabinet_count": 128,
- "online_count": 125
-}
-```
-
-**柜子排行:**
-```json
-[
- {"cabinet_id": 1, "abstract_id": "10-00000001", "today_energy": 320.5},
- {"cabinet_id": 2, "abstract_id": "10-00000002", "today_energy": 285.0}
-]
-```
-
-**趋势(日):**
-```json
-[
- {"date": "2026-07-01", "energy": 3750.50, "charge_count": 423},
- {"date": "2026-06-30", "energy": 3680.20, "charge_count": 415}
-]
-```
-
-## 前端页面
-
-### 能耗管理页
-
-**顶部数据卡片:**
-```
-┌─────────┐ ┌───────── ┌─────────┐ ┌─────────
-│今日能耗 │ │本月能耗 │ │电费估算 │ │在线柜子 │
-│3,750 kWh│ │85,200 kWh│ │¥68,160 │ │ 125 │
-└───────── └─────────┘ └───────── └─────────┘
-```
-
-**柜子能耗排行:**
-- 表格显示柜子ID、今日能耗、本月能耗
-- 支持排序
-
-**项目能耗统计:**
-- 表格显示项目名、柜子数、今日能耗、本月能耗
-
-**趋势图:**
-- 日/周/月切换
-- 折线图显示能耗趋势
-- 柱状图显示充电次数
-
-**导出按钮:**
-- 异步导出Excel
-- 文件名:`能耗统计_2026年7月.xlsx`
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
diff --git a/tasks/008-h5-user-interface.md b/tasks/008-h5-user-interface.md
deleted file mode 100644
index aa19e38..0000000
--- a/tasks/008-h5-user-interface.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# 任务008:H5用户端
-
-## 目标
-
-实现H5用户端,底部导航(首页/设备/我的),扫码进入设备详情,充电控制。
-
-## 技术栈
-
-- React + TypeScript(复用前端项目)
-- 移动端适配(viewport + rem/vw)
-- Arco Design Mobile(或自定义移动端组件)
-
-## 路由结构
-
-```
-/pms # H5首页(Dashboard)
-/pms/devices # 设备列表
-/pms/devices/:id # 设备详情(柜子视图)
-/pms/me # 我的
-/pms/login # 登录
-```
-
-**扫码入口:** `/pms/{abstract_id}` 直接跳到设备详情
-
-## 页面设计
-
-### 底部导航
-
-```
-┌─────────────────┐
-│ │
-│ 页面内容 │
-│ │
-─────────────────┤
-│ 首页 | 设备 | 我的 │
-└─────────────────┘
-```
-
-### 首页(Dashboard)
-
-**数据卡片:**
-```
-┌─────┐ ┌───── ┌─────┐ ┌─────
-│在线 │ │充电中│ │空闲 │ │故障 │
-│ 125 │ │ 45 │ │ 82 │ │ 3 │
-│柜子 │ │柜子 │ │通道 │ │通道 │
-└───── └───── └───── └─────┘
-```
-
-**今日统计:**
-- 充电次数:423次
-- 充电时长:628小时
-- 总能耗:3,750 kWh
-
-**告警通知:**
-- ⚠ 项目A-柜子3 通道12 过温
-- ⚠ 项目B-柜子7 离线
-
-**项目列表:**
-- 项目A | 3柜 | 充电28 空闲10 故障2
-- 项目B | 2柜 | 充电17 空闲15 故障0
-
-### 设备页
-
-**项目列表 → 设备列表 → 设备详情**
-
-**设备详情(柜子视图):**
-```
-┌──────────────────────────────────────────────┐
-│ 柜子ID: 10-00000000 状态: 在线 │
-├──────────────────────────────────────────────┤
-│ 仓板1 (ID:1) │
-│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐│
-│ │通道1 │ │通道2 │ │通道3 │ │通道4 │ │通道5 │ │通道6 ││
-│ │状态 │ │状态 │ │状态 │ │状态 │ │状态 │ │状态 ││
-│ │电压 │ │电压 │ │电压 │ │电压 │ │电压 │ │电压 ││
-│ │电流 │ │电流 │ │电流 │ │电流 │ │电流 │ │电流 ││
-│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘│
-──────────────────────────────────────────────┤
-│ 仓板2 (ID:2) ... │
-└──────────────────────────────────────────────┘
-```
-
-**通道详情页:**
-- 实时状态(SOC、SOH)
-- 电气参数(电压、电流、功率、能耗)
-- BMS数据(单体电压、温度、告警)
-- 充电曲线
-- 操作按钮(停止充电、开门)— 需确认弹窗
-
-### 我的页
-
-- 个人信息(姓名、手机号)
-- 修改密码
-- 退出登录
-
-## API接口
-
-```
-GET /api/h5/dashboard # 首页数据
-GET /api/h5/projects # 项目列表
-GET /api/h5/cabinets # 设备列表(按项目)
-GET /api/h5/cabinets/:id # 设备详情
-GET /api/h5/compartments/:id # 通道详情
-POST /api/h5/charge/start # 开始充电
-POST /api/h5/charge/stop # 停止充电
-POST /api/h5/door/open # 开门
-```
-
-## 质量约束
-
-1. 完成代码后执行 `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. React函数组件+Hooks
-7. 移动端适配,触摸友好
diff --git a/tasks/009-system-settings.md b/tasks/009-system-settings.md
deleted file mode 100644
index 00491c4..0000000
--- a/tasks/009-system-settings.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# 任务009:系统设置
-
-## 目标
-
-实现用户管理、操作日志查看。
-
-## 后端API
-
-### 用户管理
-```
-GET /api/users # 用户列表(分页)
-POST /api/users # 创建用户(手机号+姓名+角色)
-PUT /api/users/:id # 编辑用户
-PUT /api/users/:id/reset-password # 重置密码
-PUT /api/users/:id/status # 启用/禁用
-DELETE /api/users/:id # 删除用户
-```
-
-### 操作日志
-```
-GET /api/operation-logs # 操作日志列表
- ?user_id=xxx&action=xxx
- &start_time=xxx&end_time=xxx&page=1&page_size=20
-```
-
-## 前端页面
-
-### 用户管理页
-
-**列表显示:**
-| 手机号 | 姓名 | 角色 | 所属组织 | 状态 | 操作 |
-|--------|------|------|----------|------|------|
-| 138... | 张三 | 企业管理员 | 组织A | 启用 | 编辑/重置密码/禁用 |
-
-**创建用户弹窗:**
-- 手机号(必填,11位)
-- 姓名(选填)
-- 角色选择(总管理员/企业管理员/普通用户)
-- 所属组织(企业管理员必填)
-- 初始密码(默认123456,可修改)
-
-**编辑用户弹窗:**
-- 姓名
-- 角色
-- 所属组织
-
-**重置密码:**
-- 确认弹窗
-- 新密码(默认123456)
-
-### 操作日志页
-
-**顶部过滤:**
-- 用户下拉
-- 操作类型(登录/创建/编辑/删除/控制设备)
-- 时间范围
-
-**列表显示:**
-| 用户 | 操作 | 对象类型 | 对象 | 详情 | 时间 |
-|------|------|----------|------|------|------|
-| 张三 | 创建柜子 | 设备 | 10-00000001 | 批量添加3台 | 2026-07-01 10:30 |
-| 李四 | 开始充电 | 通道 | 通道1 | 柜子10-00000001 | 2026-07-01 10:35 |
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
diff --git a/tasks/010-security-fixes.md b/tasks/010-security-fixes.md
deleted file mode 100644
index 2443a30..0000000
--- a/tasks/010-security-fixes.md
+++ /dev/null
@@ -1,155 +0,0 @@
-# 任务010:安全修复
-
-## 目标
-
-修复代码审查发现的4个严重安全问题。
-
-## 修复清单
-
-### 1. SQL注入修复( 高优先级)
-
-**问题位置:**
-- `src/routes/operation_logs.rs:49-63`
-- `src/routes/charge_records.rs:130`
-- `src/routes/energy_stats.rs:341`
-
-**修复方案:**
-将所有 `format!` 拼接的SQL改为参数化查询。
-
-```rust
-// 错误示例(operation_logs.rs)
-let sql = format!(
- "SELECT * FROM operation_logs WHERE action = '{}' AND created_at >= '{}'",
- action.replace("'", "''"), start_time
-);
-
-// 正确示例
-let sql = "SELECT * FROM operation_logs WHERE action = $1 AND created_at >= $2";
-sqlx::query_as(sql)
- .bind(&action)
- .bind(&start_time)
-```
-
-**涉及文件:**
-- `src/routes/operation_logs.rs`
-- `src/routes/charge_records.rs`
-- `src/routes/energy_stats.rs`
-- `src/routes/downloads.rs`
-
-### 2. 密码哈希(🔴 高优先级)
-
-**问题位置:** `src/routes/auth.rs:51`
-
-**修复方案:**
-集成 `argon2` crate,密码存储和验证使用哈希。
-
-```rust
-// Cargo.toml 添加
-argon2 = "0.5"
-
-// 密码哈希(创建/重置用户时)
-use argon2::{
- password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, SaltString},
- Argon2,
-};
-
-fn hash_password(password: &str) -> Result {
- let salt = SaltString::generate(&mut OsRng);
- let argon2 = Argon2::default();
- let hash = argon2.hash_password(password.as_bytes(), &salt)?;
- Ok(hash.to_string())
-}
-
-// 密码验证(登录时)
-fn verify_password(password: &str, hash: &str) -> Result {
- let parsed_hash = PasswordHash::new(hash)?;
- let argon2 = Argon2::default();
- Ok(argon2.verify_password(password.as_bytes(), &parsed_hash).is_ok())
-}
-```
-
-**涉及文件:**
-- `src/routes/auth.rs` — 登录验证改用 `verify_password`
-- `src/routes/users.rs` — 创建/重置密码改用 `hash_password`
-- `src/db/migrate.rs` — users表password字段长度改为255(argon2哈希较长)
-
-### 3. 组织级数据隔离(🔴 高优先级)
-
-**问题位置:**
-- `src/routes/organizations.rs` 全部路由
-- `src/routes/charge_records.rs`
-- `src/routes/device_logs.rs`
-- `src/routes/energy_stats.rs`
-
-**修复方案:**
-在 `CurrentUser` 中携带 `organization_id`,查询时自动过滤。
-
-```rust
-// middleware/auth.rs 修改
-pub struct CurrentUser {
- pub user_id: i64,
- pub role_level: i32, // 0普通 1企业管理员 2总管理员
- pub organization_id: Option, // 新增
- pub permissions: HashSet,
-}
-
-// 查询时根据角色自动过滤
-pub fn apply_org_filter(query: &str, user: &CurrentUser) -> String {
- if user.role_level >= 2 {
- // 总管理员看全部
- query.to_string()
- } else if let Some(org_id) = user.organization_id {
- // 企业管理员只看本组织
- format!("{} WHERE organization_id = {}", query, org_id)
- } else {
- format!("{} WHERE 1=0", query) // 无组织用户看不了
- }
-}
-```
-
-**涉及文件:**
-- `src/middleware/auth.rs` — CurrentUser增加organization_id
-- `src/routes/organizations.rs` — 所有查询加组织过滤
-- `src/routes/charge_records.rs` — 加组织过滤
-- `src/routes/device_logs.rs` — 加组织过滤
-- `src/routes/energy_stats.rs` — 加组织过滤
-
-### 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`
-
-**修复方案:**
-为每个路由添加对应的 `check_permission` 调用。
-
-```rust
-// 示例
-#[get("/api/charge-records")]
-async fn list_charge_records(
- user: CurrentUser,
- pool: State,
- query: Query,
-) -> Result>> {
- auth::check_permission(&user, "charge_record:view")?; // 新增
- // ... 原有逻辑
-}
-```
-
-**权限码对应:**
-- 充电记录 → `charge_record:view`
-- 设备日志 → `device_log:view`
-- 能耗统计 → `energy:view`
-- 下载中心 → `charge_record:export` 或 `energy:export`
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
-7. 修复后重新运行审查清单中的安全项目,确保全部通过
diff --git a/tasks/011-code-quality-improvements.md b/tasks/011-code-quality-improvements.md
deleted file mode 100644
index de1d007..0000000
--- a/tasks/011-code-quality-improvements.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# 任务011:代码质量优化
-
-## 目标
-
-修复代码审查发现的警告问题,提升代码质量。
-
-## 修复清单
-
-### 1. 拆分大文件( 中优先级)
-
-**organizations.rs(693行)**
-拆分为:
-- `src/routes/organizations.rs` — 组织CRUD(~150行)
-- `src/routes/projects.rs` — 项目CRUD(~150行)
-- `src/routes/cabinets.rs` — 设备CRUD+详情(~200行)
-- `src/routes/cabinet_tree.rs` — 树状结构(~100行)
-
-**users.rs(495行)**
-拆分为:
-- `src/routes/users.rs` — 用户CRUD(~200行)
-- `src/routes/user_operations.rs` — 操作日志辅助函数(~100行)
-
-**devices/index.tsx(631行)**
-拆分为:
-- `src/pages/devices/index.tsx` — 主页面(~150行)
-- `src/pages/devices/OrganizationTree.tsx` — 组织树组件(~150行)
-- `src/pages/devices/CabinetGrid.tsx` — 柜子卡片网格(~150行)
-- `src/pages/devices/AddCabinetModal.tsx` — 添加柜子弹窗(~100行)
-
-### 2. JWT密钥安全检查(🟡 中优先级)
-
-**位置:** `src/middleware/auth.rs:28`
-
-**修复方案:**
-启动时检测默认密钥,打印WARN日志。
-
-```rust
-// config.rs 添加
-impl Config {
- pub fn validate(&self) -> Result<()> {
- if self.jwt_secret == JWT_SECRET_DEFAULT {
- tracing::warn!("⚠️ 使用默认JWT密钥,生产环境请设置JWT_SECRET环境变量");
- }
- Ok(())
- }
-}
-
-// main.rs 启动时调用
-let config = Config::from_env()?;
-config.validate()?; // 新增
-```
-
-### 3. 修复IconFolderOpen构建错误(🟡 中优先级)
-
-**位置:** `src/pages/devices/index.tsx:33`
-
-**修复方案:**
-替换为可用图标。
-
-```tsx
-// 错误
-import { IconFolderOpen } from '@arco-design/web-react/icon';
-
-// 修复(选择存在的图标)
-import { IconFolder } from '@arco-design/web-react/icon';
-// 或
-import { IconApps } from '@arco-design/web-react/icon';
-```
-
-### 4. 清理clippy警告(🟢 低优先级)
-
-**修复方案:**
-```bash
-cd software/server
-cargo clippy --fix --allow-dirty
-```
-
-自动修复大部分clippy警告(collapsible_if、manual_unwrap_or_default等)。
-
-### 5. H5充电曲线数据(🟢 低优先级)
-
-**位置:** `src/h5/pages/compartment-detail.tsx:156`
-
-**修复方案:**
-添加TODO注释,标记为待后端提供数据后替换。
-
-```tsx
-// TODO: 待后端提供充电曲线API后替换为真实数据
-const mockChartData = [30, 45, 55, ...];
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
-7. 拆分后的文件职责单一,无循环依赖
diff --git a/tasks/012-permission-checks-supplement.md b/tasks/012-permission-checks-supplement.md
deleted file mode 100644
index 58bb356..0000000
--- a/tasks/012-permission-checks-supplement.md
+++ /dev/null
@@ -1,172 +0,0 @@
-# 任务012:补充权限校验
-
-## 目标
-
-为 cabinets.rs、projects.rs、organizations.rs 三个模块补充完整的权限校验。
-
-## 问题描述
-
-第二轮代码审查发现这3个模块的handler函数签名中缺少 `CurrentUser` 参数,导致无法进行权限校验。任何登录用户都可以访问这些敏感接口。
-
-## 修复清单
-
-### 1. 权限码定义
-
-首先在权限表中添加缺失的权限码:
-
-```sql
--- 组织管理
-INSERT INTO permissions (code, name) VALUES ('org:view', '查看组织');
-INSERT INTO permissions (code, name) VALUES ('org:create', '创建组织');
-INSERT INTO permissions (code, name) VALUES ('org:edit', '编辑组织');
-INSERT INTO permissions (code, name) VALUES ('org:delete', '删除组织');
-
--- 项目管理
-INSERT INTO permissions (code, name) VALUES ('project:view', '查看项目');
-INSERT INTO permissions (code, name) VALUES ('project:create', '创建项目');
-INSERT INTO permissions (code, name) VALUES ('project:edit', '编辑项目');
-INSERT INTO permissions (code, name) VALUES ('project:delete', '删除项目');
-
--- 设备管理(补充)
-INSERT INTO permissions (code, name) VALUES ('device:edit', '编辑设备');
-```
-
-### 2. organizations.rs 修复
-
-**文件:** `src/routes/organizations.rs`
-
-**修改内容:**
-- 所有handler函数签名添加 `user: CurrentUser` 参数
-- 每个handler开头调用 `check_permission`
-
-```rust
-// 示例:获取组织列表
-#[get("/api/organizations")]
-async fn list_organizations(
- user: CurrentUser, // 新增
- pool: State,
-) -> Result>> {
- auth::check_permission(&user, "org:view")?; // 新增
- // ... 原有逻辑
-}
-
-// 示例:创建组织
-#[post("/api/organizations")]
-async fn create_organization(
- user: CurrentUser, // 新增
- pool: State,
- json: Json,
-) -> Result> {
- auth::check_permission(&user, "org:create")?; // 新增
- // ... 原有逻辑
-}
-```
-
-**需要修复的端点:**
-- `GET /api/organizations` → `org:view`
-- `POST /api/organizations` → `org:create`
-- `PUT /api/organizations/:id` → `org:edit`
-- `DELETE /api/organizations/:id` → `org:delete`
-
-### 3. projects.rs 修复
-
-**文件:** `src/routes/projects.rs`
-
-**修改内容:**
-- 所有handler函数签名添加 `user: CurrentUser` 参数
-- 每个handler开头调用 `check_permission`
-
-**需要修复的端点:**
-- `GET /api/organizations/:org_id/projects` → `project:view`
-- `POST /api/organizations/:org_id/projects` → `project:create`
-- `PUT /api/projects/:id` → `project:edit`
-- `DELETE /api/projects/:id` → `project:delete`
-
-### 4. cabinets.rs 修复
-
-**文件:** `src/routes/cabinets.rs`
-
-**修改内容:**
-- 所有handler函数签名添加 `user: CurrentUser` 参数
-- 每个handler开头调用 `check_permission`
-- `list_cabinets` 和 `get_cabinet` 需要额外验证用户是否有权访问该项目
-
-**需要修复的端点:**
-- `GET /api/projects/:project_id/cabinets` → `device:view` + 项目权限验证
-- `POST /api/cabinets` → `device:create`
-- `PUT /api/cabinets/:id` → `device:edit`
-- `DELETE /api/cabinets/:id` → `device:delete`
-- `POST /api/cabinets/:id/regenerate-auth` → `device:edit`
-- `GET /api/cabinets/:id` → `device:view` + 项目权限验证
-- `GET /api/cabinets/options` → `device:view`
-- `GET /api/cabin-boards/options` → `device:view`
-- `GET /api/compartments/options` → `device:view`
-
-**项目权限验证逻辑:**
-```rust
-// 验证用户是否有权访问指定项目
-async fn check_project_access(
- user: &CurrentUser,
- pool: &MySqlPool,
- project_id: i64,
-) -> Result<()> {
- if user.role_level >= 2 {
- // 总管理员可访问所有项目
- return Ok(());
- }
-
- if let Some(org_id) = user.organization_id {
- // 验证项目是否属于用户的组织
- let project: Option<(i64,)> = sqlx::query_as(
- "SELECT id FROM projects WHERE id = ? AND organization_id = ?"
- )
- .bind(project_id)
- .bind(org_id)
- .fetch_optional(pool)
- .await?;
-
- if project.is_none() {
- return Err(AppError::Forbidden("无权访问该项目".into()));
- }
- } else {
- return Err(AppError::Forbidden("无组织关联".into()));
- }
-
- Ok(())
-}
-```
-
-### 5. users.rs 修复(额外)
-
-**文件:** `src/routes/users.rs`
-
-**修改内容:**
-- `list_users` 添加组织隔离,企业管理员只能看到本组织的用户
-
-```rust
-// 修改 list_users 查询
-let users = if user.role_level >= 2 {
- // 总管理员看所有用户
- sqlx::query_as("SELECT * FROM users")
- .fetch_all(&pool)
- .await?
-} else if let Some(org_id) = user.organization_id {
- // 企业管理员只看本组织用户
- sqlx::query_as("SELECT * FROM users WHERE organization_id = ?")
- .bind(org_id)
- .fetch_all(&pool)
- .await?
-} else {
- vec![] // 无组织用户看不到任何人
-};
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
-7. 修复后重新运行 `cargo test`(如有测试)确保无回归
diff --git a/tasks/013-critical-fixes.md b/tasks/013-critical-fixes.md
deleted file mode 100644
index 4b42ee5..0000000
--- a/tasks/013-critical-fixes.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# 任务013:严重问题修复
-
-## 目标
-
-修复全面代码审查发现的5个严重问题。
-
-## 修复清单
-
-### 1. 设备指令接口权限校验( 高优先级)
-
-**位置:** `src/tcp/commands.rs`
-
-**问题:** HTTP API接口缺少权限校验,任何登录用户都能控制设备。
-
-**修复方案:**
-```rust
-// 修改 send_command handler
-pub async fn send_command(
- user: CurrentUser, // 新增
- State(state): State,
- Path(dev_id): Path,
- Json(body): Json,
-) -> Result, AppError> {
- auth::check_permission(&user, "device:operate")?; // 新增
- // ... 原有逻辑
-}
-
-// 修改 get_online_devices handler
-pub async fn get_online_devices(
- user: CurrentUser, // 新增
- State(state): State,
-) -> Result, AppError> {
- auth::check_permission(&user, "device:view")?; // 新增
- // ... 原有逻辑
-}
-```
-
-### 2. 组织树接口权限校验和数据隔离(🔴 高优先级)
-
-**位置:** `src/routes/cabinet_tree.rs`
-
-**问题:** 组织树接口无权限校验,且未做数据隔离,用户能看到所有组织的数据。
-
-**修复方案:**
-```rust
-pub async fn get_organization_tree(
- user: CurrentUser, // 新增
- State(state): State,
-) -> Result, AppError> {
- auth::check_permission(&user, "device:view")?; // 新增
-
- let db = &state.mysql;
-
- // 根据角色过滤数据
- let orgs = if user.role_level >= 2 {
- // 总管理员看所有组织
- sqlx::query_as::<_, OrganizationRow>("SELECT id, name FROM organizations ORDER BY id")
- .fetch_all(db)
- .await?
- } else if let Some(org_id) = user.organization_id {
- // 企业管理员只看本组织
- sqlx::query_as::<_, OrganizationRow>(
- "SELECT id, name FROM organizations WHERE id = ? ORDER BY id"
- )
- .bind(org_id)
- .fetch_all(db)
- .await?
- } else {
- vec![] // 无组织用户看不到数据
- };
-
- // ... 构建树状结构
-}
-```
-
-### 3. 默认密码硬编码(🔴 中优先级)
-
-**位置:** `src/routes/users.rs`
-
-**问题:** 默认密码 "123456" 硬编码在代码中。
-
-**修复方案:**
-```rust
-// config.rs 添加默认密码配置
-pub const DEFAULT_PASSWORD: &str = "123456"; // 或从环境变量读取
-
-// users.rs 使用配置
-use crate::config::DEFAULT_PASSWORD;
-
-// 创建用户时
-let password = body.password.unwrap_or_else(|| DEFAULT_PASSWORD.to_string());
-let hashed = auth::hash_password(&password)?;
-```
-
-### 4. auth_str安全码明文打印到日志(🔴 中优先级)
-
-**位置:** `src/routes/organizations.rs` 或 `src/tcp/handler.rs`
-
-**问题:** auth_str安全码明文打印到日志,存在泄露风险。
-
-**修复方案:**
-```rust
-// 修改日志输出,只打印前2位+***
-tracing::info!(
- "生成安全码 id={} auth_str={}***",
- id,
- &auth_str[..2]
-);
-
-// 或完全不打印auth_str
-tracing::info!("生成安全码 id={}", id);
-```
-
-### 5. 前端刷新后用户信息丢失(🔴 中优先级)
-
-**位置:** `src/stores/auth.ts` 或 `src/App.tsx`
-
-**问题:** 页面刷新后,虽然token在localStorage,但用户信息(permissions等)未恢复,导致权限按钮消失。
-
-**修复方案:**
-```tsx
-// stores/auth.ts 添加 restore 方法
-const restoreAuth = async () => {
- const token = localStorage.getItem('token');
- if (!token) return false;
-
- try {
- const user = await api.get('/auth/me');
- setToken(token);
- setUser(user);
- setPermissions(user.permissions);
- return true;
- } catch {
- localStorage.removeItem('token');
- return false;
- }
-};
-
-// App.tsx 或 AdminLayout.tsx 中调用
-useEffect(() => {
- restoreAuth();
-}, []);
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy` + `tsc --noEmit`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,React函数组件+Hooks
-7. 修复后重新运行审查清单中的安全项目,确保全部通过
diff --git a/tasks/014-mimo-review-fixes.md b/tasks/014-mimo-review-fixes.md
deleted file mode 100644
index 88e2521..0000000
--- a/tasks/014-mimo-review-fixes.md
+++ /dev/null
@@ -1,284 +0,0 @@
-# 任务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接口必须添加权限校验
diff --git a/tasks/015-nemotron-review-fixes.md b/tasks/015-nemotron-review-fixes.md
deleted file mode 100644
index 1bf72ed..0000000
--- a/tasks/015-nemotron-review-fixes.md
+++ /dev/null
@@ -1,219 +0,0 @@
-# 任务015:Nemotron审查问题修复
-
-## 目标
-
-修复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,
-) -> Result, 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,
- Path(id): Path,
-) -> Result, 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::>()
-})))
-```
-
-### 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接口必须添加权限校验和组织数据隔离
diff --git a/tasks/016-tcp-service-refactor.md b/tasks/016-tcp-service-refactor.md
deleted file mode 100644
index 7753e31..0000000
--- a/tasks/016-tcp-service-refactor.md
+++ /dev/null
@@ -1,324 +0,0 @@
-# 任务016:TCP服务重构
-
-## 目标
-
-重构TCP服务,拆分为API服务和设备服务,使用Redis做服务发现和消息队列。
-
-## 架构设计
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ API服务 (api-server) — 主服务 │
-│ - HTTP API(后台管理 + H5)端口: 3000 │
-│ - 前端轮询刷新(每5秒) │
-│ - 业务逻辑处理(验证/存DB/告警) │
-│ - 节点管理(设备服务注册/心跳) │
-│ - 后台协程处理Redis队列 │
-└─────────────────────────────────────────────────────────────┘
- ↑ Redis LIST ↑ Redis LIST
- │ device:report:{imei} │ device:cmd:{imei}
- │ (设备上报) │ (平台指令)
- │ │
-┌─────────────────────────────────────────────────────────────┐
-│ 设备服务 (device-server) — 轻量化 │
-│ - TCP监听: 3002(设备长连接) │
-│ - 只处理通信和指令解析 │
-│ - 不存DB、不验证签名、不处理业务 │
-│ - 注册到API服务(启动/心跳/注销) │
-│ - Redis连接注册: device:{imei} → {node_id, ip, port} │
-└─────────────────────────────────────────────────────────────┘
-```
-
-## 核心设计原则
-
-1. **TCP服务轻量化** — 只处理TCP通信和指令解析,不处理业务逻辑
-2. **Redis做缓冲** — 服务间通信用Redis LIST,不用HTTP直接调用
-3. **全异步处理** — 所有指令异步处理,msg_id匹配响应
-4. **协程处理任务** — API服务用tokio::spawn处理Redis队列消息
-
-## 重构清单
-
-### 1. 项目结构拆分
-
-**当前:** 单服务 `software/server/`
-
-**重构后:**
-```
-software/
-├── api-server/ # API服务(主服务)
-│ ├── Cargo.toml
-│ ├── config.toml
-│ └── src/
-│ ├── main.rs
-│ ├── config.rs
-│ ├── routes/
-│ │ ├── devices.rs # 设备管理API
-│ │ ├── nodes.rs # 节点管理API
-│ │ └── ...
-│ ├── workers/ # 后台协程
-│ │ ├── report_worker.rs # 处理设备上报
-│ │ └── reply_worker.rs # 处理设备响应
-│ ├── middleware/
-│ ── h5/
-├── device-server/ # 设备服务(轻量化)
-│ ├── Cargo.toml
-│ ├── config.toml
-│ └── src/
-│ ├── main.rs
-│ ├── config.rs
-│ ├── tcp/
-│ │ ├── server.rs # TCP监听
-│ │ ├── connection.rs # 连接管理(内存HashMap)
-│ │ ├── protocol.rs # 协议解析
-│ │ └── handler.rs # 消息处理(解析+转发Redis)
-│ └── redis.rs # Redis连接注册
-```
-
-**说明:** 暂不提取shared crate,协议定义等代码先复制到两个服务。
-
-### 2. 配置文件(config.toml)
-
-**api-server/config.toml:**
-```toml
-[server]
-host = "0.0.0.0"
-port = 3000
-
-[database]
-url = "mysql://root:password@10.8.0.252:3306/pms_dev"
-
-[redis]
-url = "redis://:password@10.8.0.252:6379/5"
-
-[jwt]
-secret = "pms-dev-secret-change-me-in-production"
-
-[export]
-dir = "./exports"
-
-[node]
-heartbeat_timeout = 180
-```
-
-**device-server/config.toml:**
-```toml
-[server]
-host = "0.0.0.0"
-tcp_port = 3002
-node_id = "node-1"
-
-[redis]
-url = "redis://:password@10.8.0.252:6379/5"
-
-[api_server]
-url = "http://127.0.0.1:3000"
-heartbeat_interval = 60
-
-[device]
-register_ttl = 180
-```
-
-### 3. 服务间通信(Redis LIST)
-
-**设备上报(设备→API):**
-```rust
-// 设备服务:收到设备消息后推送到Redis
-async fn forward_to_api(redis: &Redis, imei: &str, msg: &DeviceMessage) {
- redis.lpush(&format!("device:report:{}", imei), serde_json::to_string(msg)).await;
-}
-
-// API服务:后台协程处理上报
-async fn process_reports(redis: Redis, mysql: MySqlPool) {
- loop {
- let (_, msg_json) = redis.brpop(&["device:report:*"], 0).await;
- let msg: DeviceMessage = serde_json::from_str(&msg_json).unwrap();
- tokio::spawn(handle_report(msg, mysql.clone())); // 异步处理
- }
-}
-```
-
-**平台指令(API→设备):**
-```rust
-// API服务:下发指令推送到Redis
-async fn send_command(redis: &Redis, imei: &str, cmd: DeviceCommand) {
- redis.lpush(&format!("device:cmd:{}", imei), serde_json::to_string(&cmd)).await;
-}
-
-// 设备服务:阻塞弹出指令,发送到设备
-async fn process_commands(redis: Redis, pool: ConnectionPool) {
- loop {
- let (key, cmd_json) = redis.brpop(&["device:cmd:*"], 0).await;
- let imei = extract_imei(&key);
- let cmd: DeviceCommand = serde_json::from_str(&cmd_json).unwrap();
-
- // 发送到设备(异步等待响应)
- if let Some(conn) = pool.get(&imei) {
- conn.send(cmd).await;
- }
- }
-}
-```
-
-**指令响应(设备→API):**
-```rust
-// 设备服务:收到设备响应后推送到Redis
-async fn forward_reply(redis: &Redis, imei: &str, reply: DeviceResponse) {
- redis.lpush(&format!("device:reply:{}", imei), serde_json::to_string(&reply)).await;
-}
-
-// API服务:后台协程匹配msg_id
-async fn process_replies(redis: Redis, pending: PendingCommands) {
- loop {
- let (_, reply_json) = redis.brpop(&["device:reply:*"], 0).await;
- let reply: DeviceResponse = serde_json::from_str(&reply_json).unwrap();
- if let Some(tx) = pending.remove(&reply.msg_id) {
- tx.send(reply).ok();
- }
- }
-}
-```
-
-### 4. 节点管理(API服务)
-
-**节点注册:**
-```rust
-// POST /api/nodes/register
-async fn register_node(Json(req): Json) -> Result> {
- // 记录节点信息到内存/DB
- nodes.insert(req.node_id.clone(), NodeInfo {
- id: req.node_id,
- ip: req.ip,
- tcp_port: req.tcp_port,
- status: "online",
- last_heartbeat: Instant::now(),
- });
- Ok(Json(json!({"suc": 1})))
-}
-```
-
-**节点心跳:**
-```rust
-// POST /api/nodes/:node_id/heartbeat
-async fn node_heartbeat(Path(node_id): Path) -> Result> {
- if let Some(node) = nodes.get_mut(&node_id) {
- node.last_heartbeat = Instant::now();
- }
- Ok(Json(json!({"suc": 1})))
-}
-```
-
-**节点注销:**
-```rust
-// POST /api/nodes/:node_id/deregister
-async fn deregister_node(Path(node_id): Path) -> Result> {
- nodes.remove(&node_id);
- Ok(Json(json!({"suc": 1})))
-}
-```
-
-**节点列表:**
-```rust
-// GET /api/nodes
-async fn list_nodes() -> Result>> {
- Ok(Json(json!(nodes.values().collect::>())))
-}
-```
-
-### 5. 设备服务注册流程
-
-```rust
-// 设备服务启动时
-async fn startup(api_url: &str, node_id: &str, ip: &str, tcp_port: u16) {
- // 1. 注册到API服务
- let client = reqwest::Client::new();
- client.post(&format!("{}/api/nodes/register", api_url))
- .json(&json!({ "node_id": node_id, "ip": ip, "tcp_port": tcp_port }))
- .send().await;
-
- // 2. 启动心跳协程(每60秒)
- let api_url = api_url.to_string();
- let node_id = node_id.to_string();
- tokio::spawn(async move {
- let mut interval = tokio::time::interval(Duration::from_secs(60));
- loop {
- interval.tick().await;
- client.post(&format!("{}/api/nodes/{}/heartbeat", api_url, node_id))
- .send().await;
- }
- });
-}
-
-// 设备服务关闭时
-async fn shutdown(api_url: &str, node_id: &str) {
- let client = reqwest::Client::new();
- client.post(&format!("{}/api/nodes/{}/deregister", api_url, node_id))
- .send().await;
-}
-```
-
-### 6. 异步指令处理(msg_id匹配)
-
-```rust
-// API服务下发指令
-async fn send_command_to_device(imei: &str, cmd: DeviceCommand) -> Result {
- let (tx, rx) = oneshot::channel();
- pending_commands.insert(cmd.msg_id, tx);
-
- // 推送到Redis队列
- redis.lpush(&format!("device:cmd:{}", imei), serde_json::to_string(&cmd)).await;
-
- // 等待响应(超时60秒)
- match tokio::time::timeout(Duration::from_secs(60), rx).await {
- Ok(Ok(response)) => Ok(response),
- Ok(Err(_)) => Err(AppError::Internal("通道关闭".into())),
- Err(_) => {
- pending_commands.remove(&cmd.msg_id);
- Err(AppError::Timeout("设备响应超时".into()))
- }
- }
-}
-```
-
-### 7. 自动充电流程
-
-```rust
-// API服务收到 bat_in 上报
-async fn handle_bat_in(imei: &str, channel: &str, bms: &BmsData) {
- // 1. 检查通道是否已有电池
- if let Some(old_battery) = battery_map.get(channel) {
- // 旧电池强制结束
- record_charge_complete(channel, old_battery, ChargeEndReason::Forced);
- }
-
- // 2. 记录新电池
- battery_map.insert(channel.to_string(), battery.clone());
-
- // 3. 验证电池合规性
- match validate_battery(bms) {
- Ok(()) => {
- // 4. 自动下发充电指令
- send_command_to_device(imei, DeviceCommand {
- act: "on".to_string(),
- id: channel.to_string(),
- msg_id: generate_msg_id(),
- }).await;
- }
- Err(e) => {
- // 验证失败,记录日志
- tracing::warn!("电池验证失败: {}", e);
- }
- }
-}
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
-7. 异步代码无阻塞操作
-8. 指令处理必须异步,msg_id匹配响应
-9. TCP服务不处理业务逻辑,只负责通信和解析
diff --git a/tasks/017-fix-review-issues.md b/tasks/017-fix-review-issues.md
deleted file mode 100644
index 0fa6605..0000000
--- a/tasks/017-fix-review-issues.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# 任务017:重构审查问题修复
-
-## 目标
-
-修复TCP服务重构审查发现的P0/P1问题。
-
-## 修复清单
-
-### P0:Redis类型冲突
-
-**位置:** `device-server/src/main.rs`
-
-**问题:** `register_node`用`SET`(string类型),`heartbeat`用`HSET`(hash类型),同一key操作触发`WRONGTYPE`错误。
-
-**修复:** 统一用HSET:
-```rust
-// 节点注册
-async fn register_node(redis: &Redis, node_id: &str, ip: &str, tcp_port: u16) {
- redis.hset(&format!("node:{}", node_id), &[
- ("ip", ip),
- ("tcp_port", &tcp_port.to_string()),
- ("status", "online"),
- ("last_heartbeat", &Instant::now().elapsed().as_secs().to_string()),
- ]).await;
- redis.expire(&format!("node:{}", node_id), 300).await;
-}
-
-// 节点心跳
-async fn heartbeat(redis: &Redis, node_id: &str) {
- redis.hset(&format!("node:{}", node_id), &[
- ("last_heartbeat", &Instant::now().elapsed().as_secs().to_string()),
- ]).await;
- redis.expire(&format!("node:{}", node_id), 300).await;
-}
-```
-
-### P1:节点管理双轨制
-
-**问题:** API服务维护内存`NodeRegistry`,设备服务直接写Redis,两者不关联。
-
-**修复方案A(推荐):** API服务从Redis读取节点信息
-```rust
-// API服务节点列表
-async fn list_nodes(redis: &Redis) -> Result>> {
- let keys = redis.keys("node:*").await?;
- let mut nodes = Vec::new();
- for key in keys {
- let node_id = key.strip_prefix("node:").unwrap();
- let info: HashMap = redis.hgetall(&key).await?;
- nodes.push(NodeInfo {
- id: node_id.to_string(),
- ip: info.get("ip").cloned().unwrap_or_default(),
- tcp_port: info.get("tcp_port").and_then(|p| p.parse().ok()).unwrap_or(0),
- status: info.get("status").cloned().unwrap_or_default(),
- last_heartbeat: info.get("last_heartbeat").cloned().unwrap_or_default(),
- });
- }
- Ok(Json(nodes))
-}
-```
-
-**修复方案B:** 设备服务通过HTTP注册到API服务
-- 设备服务启动时POST到`/api/nodes/register`
-- API服务存内存
-- 心跳POST到`/api/nodes/:id/heartbeat`
-- 注销POST到`/api/nodes/:id/deregister`
-
-### P2:配置系统
-
-**问题:** 代码用`dotenvy`读环境变量,config.toml形同虚设。
-
-**修复:** 二选一
-- 方案A:用`toml` crate读config.toml
-- 方案B:删除config.toml,只用.env
-
-### P3:协议结构体重复
-
-**问题:** `DeviceMessage`等结构体在两个服务中重复定义。
-
-**修复:** 暂不处理,后续提取shared crate时统一。
-
-### P4:限流器同步Mutex
-
-**问题:** 限流器用`std::sync::Mutex`,不符合异步规范。
-
-**修复:** 改用`tokio::sync::Mutex`或`RwLock`。
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
diff --git a/tasks/018-code-optimizations.md b/tasks/018-code-optimizations.md
deleted file mode 100644
index 912301e..0000000
--- a/tasks/018-code-optimizations.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# 任务018:代码优化
-
-## 目标
-
-完成审查提出的3个改进建议。
-
-## 优化清单
-
-### 1. KEYS → SCAN
-
-**位置:** `api-server/src/routes/nodes.rs` 或 `api-server/src/commands.rs`
-
-**当前:**
-```rust
-let keys = redis.keys("node:*").await?;
-```
-
-**改进:**
-```rust
-async fn scan_nodes(redis: &redis::aio::ConnectionManager) -> Result> {
- let mut nodes = Vec::new();
- let mut cursor = 0i64;
- loop {
- let (new_cursor, keys): (i64, Vec) = redis
- .scan(cursor)
- .await?;
- nodes.extend(keys);
- if new_cursor == 0 { break; }
- cursor = new_cursor;
- }
- Ok(nodes)
-}
-```
-
-### 2. JWT常量去重
-
-**位置:** `api-server/src/middleware/auth.rs` 和 `api-server/src/config.rs`
-
-**当前:** `JWT_SECRET_DEFAULT` 在两个文件重复定义
-
-**改进:** 只在 `config.rs` 定义,`auth.rs` 从 `Config` 读取
-
-### 3. rand API更新
-
-**位置:** `device-server/src/main.rs` 或 `api-server/src/routes/organizations.rs`
-
-**当前:** 可能用了旧版 `rand::thread_rng()` 或 `gen_range`
-
-**改进:** 确认使用 rand 0.8+ API:
-```rust
-use rand::Rng;
-let mut rng = rand::thread_rng();
-let value = rng.gen_range(0..CHARSET.len());
-```
-
-## 质量约束
-
-1. 完成代码后执行 `cargo check` + `cargo clippy`,零报错零警告
-2. 分层拆分,单函数≤80行,命名语义化,完整注释
-3. 所有外部IO/网络请求异常捕获,禁止裸panic
-4. 分支逻辑全覆盖,不遗漏兜底分支
-5. 常量抽离,不使用废弃API
-6. Rust内存安全,合理管理所有权,禁用unsafe无合理理由
diff --git a/tasks/review-code-quality.md b/tasks/review-code-quality.md
deleted file mode 100644
index 79e30b9..0000000
--- a/tasks/review-code-quality.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# 代码审查报告
-
-**审查日期**: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`。
-
----
-
-## 警告(🟡 建议修复)
-
-### 警告 1:organizations.rs 文件过长(693 行)
-- **位置**:`src/routes/organizations.rs`
-- **描述**:单个文件 693 行,包含组织/项目/柜子 CRUD + 树状结构 + 柜子详情,职责过多。
-- **建议**:拆分为 `organizations.rs`、`projects.rs`、`cabinets.rs` 三个文件。
-
-### 警告 2:users.rs 文件过长(495 行)
-- **位置**:`src/routes/users.rs`
-- **描述**:包含用户 CRUD、密码重置、状态管理、操作日志记录,超过 80 行/函数约束(部分辅助函数虽短,但整体文件过大)。
-- **建议**:将 `log_operation` 辅助函数提取到独立模块。
-
-### 警告 3:JWT 默认密钥在生产环境可能泄露
-- **位置**:`src/middleware/auth.rs:28`
-- **描述**:`JWT_SECRET_DEFAULT = "pms-dev-secret-change-me-in-production"` 如果环境变量未设置,将使用此默认值。
-- **建议**:启动时若检测到默认密钥,打印 WARN 日志;或在 `config.rs` 中将 JWT_SECRET 设为必填。
-
-### 警告 4:energy_stats 导出 LIMIT/OFFSET 同样存在 format! 拼接
-- **位置**:`src/routes/energy_stats.rs:341`
-- **描述**:与 charge_records.rs 相同的问题,LIMIT/OFFSET 通过 `format!` 拼接。
-- **建议**:统一使用参数化绑定。
-
-### 警告 5:devices/index.tsx 文件过长(631 行)
-- **位置**:`src/pages/devices/index.tsx`
-- **描述**:单个 TSX 文件 631 行,包含组织树、柜子列表、CRUD 弹窗、右键菜单、批量添加等全部逻辑。
-- **建议**:提取 `OrganizationalTree.tsx`、`CabinetGrid.tsx`、`AddCabinetModal.tsx` 为独立组件。
-
-### 警告 6:Pre-existing 构建错误(IconFolderOpen)
-- **位置**:`src/pages/devices/index.tsx:33`
-- **描述**:`IconFolderOpen` 从 `@arco-design/web-react/icon` 导入,但该版本未导出此图标。导致 `vite build` 失败。
-- **风险**:前端无法构建生产包。
-- **建议**:替换为 `IconFolder` 或其他可用图标。
-
-### 警告 7:clippy 警告 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)
diff --git a/tasks/review-fix-verification-report.md b/tasks/review-fix-verification-report.md
deleted file mode 100644
index 4ead9ff..0000000
--- a/tasks/review-fix-verification-report.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# 重构修复验证报告
-
-## 总体评价
-
-**评分: 9/10** -- 所有 P0/P1 问题已正确修复,代码质量良好,编译零报错零警告。存在少量可改进点但不影响推送。
-
-## P0/P1 问题验证
-
-| # | 问题 | 状态 | 验证详情 |
-|---|------|------|----------|
-| 1 | Redis 节点信息统一用 HSET | **已修复** | `api-server/src/commands.rs:76-84` 使用 `HSET` 写入 `node:{node_id}` hash;`device-server/src/main.rs:77-92` 注册时同样使用 `HSET`。两端数据结构一致,无类型冲突。 |
-| 2 | 心跳时 TTL 自动续期 | **已修复** | `api-server/src/commands.rs:116-120` 心跳时调用 `EXPIRE` 刷新 300s TTL;`device-server/src/main.rs:119-123` 同理。两端均保证活跃节点不会被过期清理。 |
-| 3 | NodeRegistry 用 Redis 存储 | **已修复** | `api-server/src/commands.rs:57-59` `NodeRegistry` 持有 `redis::aio::ConnectionManager`,所有读写操作均通过 Redis(`HSET`/`HGETALL`/`KEYS`/`DEL`),无内存缓存。 |
-| 4 | `GET /api/nodes` 正确返回节点列表 | **已修复** | `api-server/src/nodes.rs:55-70` 从 Redis 读取节点,返回 `node_id`、`ip`、`tcp_port` 字段,响应格式 `{"nodes": [...], "count": N}`。 |
-| 5 | `is_device_online()` 正确查询 Redis | **已修复** | `api-server/src/commands.rs:159-167` 通过 `EXISTS device:online:{imei}` 查询,返回 `bool`,失败时兜底 `false`。 |
-
-## 配置系统
-
-| 检查项 | 状态 | 详情 |
-|--------|------|------|
-| 删除无用 config.toml | **通过** | `software/` 目录下无 `config.toml` 文件 |
-| 配置从 .env 读取 (dotenvy) | **通过** | `api-server/src/config.rs:29` 和 `device-server/src/config.rs:18` 均调用 `dotenvy::dotenv()` |
-| 无明文密码硬编码 | **通过** | `JWT_SECRET_DEFAULT` 值为 `"pms-dev-secret-change-me-in-production"`,明确标注需生产替换,且 `Config::validate()` 会输出警告日志。`DEFAULT_PASSWORD = "123456"` 仅用于用户管理初始密码/重置密码,属业务常量。 |
-
-## 异步规范
-
-| 检查项 | 状态 | 详情 |
-|--------|------|------|
-| 限流器用 tokio::sync::Mutex | **通过** | `report_worker.rs:14` 导入 `tokio::sync::Mutex`,`RateLimiter` 的 `attempts` 字段使用该类型。全项目 grep 确认无 `std::sync::Mutex` 使用。 |
-| 无阻塞操作 | **通过** | 所有 Redis 操作使用 `query_async`,MySQL 操作使用 `sqlx::query().execute().await`,无同步阻塞调用。 |
-| 后台协程正确处理 Redis 队列 | **通过** | `report_worker.rs` 使用 `BRPOP device:reports`,`reply_worker.rs` 使用 `BRPOP device:replies`,`device-server/main.rs` 使用 `BRPOP device:commands`。错误时 sleep 1s 重试,避免紧密循环。 |
-
-## 代码质量
-
-### 编译检查
-
-| 服务 | cargo check | cargo clippy |
-|------|-------------|--------------|
-| api-server | 零报错 | 零警告 |
-| device-server | 零报错 | 零警告 |
-
-### 函数行数
-
-所有函数均 <= 80 行。较大的函数:
-- `process_report` (`report_worker.rs:91-116`): 25 行
-- `handle_login` (`report_worker.rs:174-239`): 65 行
-- `register_node` (`device-server/main.rs:73-105`): 32 行
-- `consume_commands` (`device-server/main.rs:132-177`): 45 行
-
-### 命名与注释
-
-所有模块均有模块级文档注释(`//!`),公开函数和结构体有 `///` 注释。命名语义清晰(如 `NodeRegistry`、`PendingCommands`、`RateLimiter`)。
-
-### 错误处理
-
-- 所有外部 IO 操作返回 `Result`,使用 `?` 传播错误
-- Redis 操作失败时使用 `unwrap_or_default()` 或 `unwrap_or(false)` 兜底
-- 无裸 `panic!` 或 `unwrap()` 用于外部 IO(仅 `expect("DeviceCommand 序列化不应失败")` 用于确定不会失败的 serde 序列化,可接受)
-
-## 新引入问题
-
-### 无阻塞性问题
-
-### 建议改进(非阻塞)
-
-1. **`KEYS` 命令在生产环境应替换为 `SCAN`**
- - 位置: `api-server/src/commands.rs:128`
- - 原因: `KEYS node:*` 在节点数量大时会阻塞 Redis
- - 建议: 改用 `SCAN` 游标迭代,或维护一个 `SET` 记录活跃节点 ID
-
-2. **`JWT_SECRET_DEFAULT` 重复定义**
- - 位置: `config.rs:6` 和 `middleware/auth.rs:33`
- - 建议: 统一到 `config.rs`,`auth.rs` 引用 `crate::config::JWT_SECRET_DEFAULT`
-
-3. **`rand::thread_rng()` 在新版 rand 中已弃用**
- - 位置: `report_worker.rs:150`
- - 当前可编译通过(使用旧版 rand API),但升级 rand 后需改为 `rand::rng()`
-
-## 总结
-
-**可以推送。** 所有 P0/P1 问题已修复,两个服务编译零报错零警告,代码结构清晰,错误处理完善。上述 3 个改进建议为低优先级,可在后续迭代中处理。
diff --git a/tasks/review-fix-verification.md b/tasks/review-fix-verification.md
deleted file mode 100644
index 77794fe..0000000
--- a/tasks/review-fix-verification.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# 代码审查任务:重构修复验证
-
-## 目标
-
-审查重构修复后的代码,确认P0/P1问题已解决,无新引入问题。
-
-## 审查范围
-
-### API服务
-`software/api-server/src/` 目录下所有文件
-
-### 设备服务
-`software/device-server/src/` 目录下所有文件
-
-## 审查清单
-
-### 1. P0/P1问题验证
-- [ ] Redis节点信息统一用HSET(无类型冲突)
-- [ ] 心跳时TTL自动续期
-- [ ] NodeRegistry用Redis存储(非内存)
-- [ ] `GET /api/nodes` 能正确返回节点列表
-- [ ] `is_device_online()` 正确查询Redis
-
-### 2. 配置系统
-- [ ] 删除了无用config.toml
-- [ ] 配置从.env读取(dotenvy)
-- [ ] 无明文密码在代码中
-
-### 3. 异步规范
-- [ ] 限流器用tokio::sync::Mutex(非std::sync::Mutex)
-- [ ] 无阻塞操作
-- [ ] 后台协程正确处理Redis队列
-
-### 4. 代码质量
-- [ ] `cargo check` 零报错
-- [ ] `cargo clippy` 零警告
-- [ ] 单函数≤80行
-- [ ] 命名语义化,完整注释
-- [ ] 错误处理完善
-
-## 输出格式
-
-```markdown
-# 重构修复验证报告
-
-## 总体评价
-[评分1-10,简要评价]
-
-## P0/P1问题验证
-[是否修复,验证结果]
-
-## 新引入问题
-[有无新问题]
-
-## 代码质量
-[编译/规范/错误处理]
-
-## 总结
-[是否可以推送]
-```
-
-## 质量约束
-
-1. 审查必须覆盖所有清单项目
-2. 每个问题必须给出具体位置和修复建议
-3. 审查报告用中文撰写
-4. 必须实际读取代码文件
diff --git a/tasks/review-full-codebase-report.md b/tasks/review-full-codebase-report.md
deleted file mode 100644
index 5836e66..0000000
--- a/tasks/review-full-codebase-report.md
+++ /dev/null
@@ -1,221 +0,0 @@
-# 全面代码审查报告
-
-## 总体评价
-
-**评分:7.5 / 10**
-
-项目整体代码质量良好,架构清晰,模块职责分明。后端 Rust 代码规范,通过了 `cargo clippy` 零警告检查;前端 TypeScript 代码通过 `tsc --noEmit` 零错误,`vite build` 构建成功。RBAC 权限模型完整,TCP 协议处理健壮。主要不足集中在安全细节、性能优化和部分功能未完成方面。
-
-## 统计
-
-- 后端文件数:29(Rust)
-- 前端文件数:35(TypeScript/TSX)
-- 后端代码行数:5,162
-- 前端代码行数:5,282
-- 总代码行数:10,444
-- 问题总数:19(严重 5 / 警告 8 / 建议 6)
-
-## 静态检查结果
-
-| 检查项 | 结果 |
-|--------|------|
-| `cargo check` | 通过 |
-| `cargo clippy` | 通过(零警告) |
-| `tsc --noEmit` | 通过(零错误) |
-| `vite build` | 通过(有 chunk 大小警告) |
-
-## 严重问题(必须修复)
-
-### 问题1:设备指令下发接口缺少权限校验
-- **位置**:`software/server/src/tcp/commands.rs:36-61`
-- **描述**:`send_command` 端点(`POST /api/devices/:dev_id/command`)虽然经过认证中间件,但未调用 `check_permission` 进行 RBAC 权限校验。任何已登录用户均可向设备下发任意指令(包括开门、供电等危险操作)。
-- **风险**:未授权用户可操控设备,造成安全隐患。
-- **建议**:在 `send_command` 函数开头添加权限校验,根据 `act` 字段判断所需权限码(如 `device:operate`、charge:open_door` 等)。
-
-### 问题2:组织树接口缺少权限校验和数据隔离
-- **位置**:`software/server/src/routes/cabinet_tree.rs:14-92`
-- **描述**:`organization_tree` 端点未提取 `CurrentUser`,无权限校验,也无组织级数据隔离。企业管理员可通过此接口看到所有组织的数据。
-- **风险**:数据泄露,违反多租户隔离原则。
-- **建议**:添加 `CurrentUser` 提取器,校验 `device:view` 权限,并根据 `role_level` 和 `organization_id` 过滤返回数据。
-
-### 问题3:默认密码 "123456" 硬编码
-- **位置**:`software/server/src/routes/users.rs:302`、`software/server/src/routes/users.rs:437`
-- **描述**:创建用户和重置密码时,默认密码硬编码为 `"123456"`。此密码过于简单,且散落在代码中。
-- **风险**:安全风险,弱密码可能被暴力破解。
-- **建议**:将默认密码抽离为配置常量,并强制用户首次登录时修改密码。至少应使用随机生成的初始密码。
-
-### 问题4:auth_str 安全码明文打印到日志
-- **位置**:`software/server/src/tcp/handler.rs:61`
-- **描述**:`handle_auth_str` 函数将生成的安全码以明文写入日志:`tracing::info!("[auth_str] dev_id={} auth_str={}", dev_id, auth_str)`。
-- **风险**:安全码泄露,攻击者可通过日志获取签名密钥。
-- **建议**:移除日志中的 `auth_str` 值,仅记录 `dev_id`,或使用脱敏处理(如仅打印前2位)。
-
-### 问题5:前端刷新后用户信息丢失
-- **位置**:`software/web/src/stores/auth.ts:71-76`
-- **描述**:`restore` 函数仅从 localStorage 恢复 token,但未调用 `/api/auth/me` 获取用户信息和权限码。刷新页面后,虽然 token 存在,但 `user` 为 null,导致前端权限判断(`hasPermission`)始终返回 false,所有权限按钮消失。
-- **风险**:用户体验严重受损,刷新后所有权限控制按钮消失。
-- **建议**:在 `restore` 中检测到 token 后,异步调用 `/api/auth/me` 恢复完整用户信息。
-
-## 警告(建议修复)
-
-### 警告1:前端打包体积过大
-- **位置**:`software/web/` 构建配置
-- **描述**:vite build 产物 JS 文件 1,314 KB(gzip 后 374 KB),超过 500 KB 警告阈值。未做代码分割。
-- **建议**:使用 `React.lazy()` + 动态 `import()` 对路由级组件进行代码分割,尤其是 H5 端和后台管理端应分离打包。
-
-### 警告2:柜子详情接口存在 N+1 查询
-- **位置**:`software/server/src/routes/cabinets.rs:284-308`
-- **描述**:`get_cabinet_detail` 先查询仓控板列表,然后对每个仓控板单独查询仓体。若柜子有 N 块仓控板,则产生 N+1 次数据库查询。
-- **建议**:使用一条 SQL 通过 JOIN 查询所有仓控板和仓体,或在应用层批量查询。
-
-### 警告3:角色列表接口存在 N+1 查询
-- **位置**:`software/server/src/routes/roles.rs:42-69`
-- **描述**:`list_roles` 对每个角色单独查询权限码,产生 N+1 次查询。
-- **建议**:使用一条 SQL 通过 JOIN 查询所有角色及其权限码。
-
-### 警告4:多处使用 `as unknown as` 类型断言
-- **位置**:`software/web/src/pages/devices/index.tsx:81,103`、`OrganizationTree.tsx:169,171`
-- **描述**:前端代码中多处使用 `as unknown as` 进行类型强转,绕过了 TypeScript 类型检查。
-- **建议**:修正 API 响应类型定义,使其与实际返回数据结构一致,消除不必要的类型断言。
-
-### 警告5:下载文件端点未实现
-- **位置**:`software/server/src/routes/downloads.rs:162`
-- **描述**:`get_download` 返回 `download_url: "/api/downloads/{id}/file"`,但此路由未在 `routes/mod.rs` 中注册,实际下载功能不可用。
-- **建议**:实现文件下载端点,使用 `axum::response::File` 或 `tower-http` 的 `ServeDir` 提供文件服务。
-
-### 警告6:`org_condition` 辅助函数未被充分使用
-- **位置**:`software/server/src/middleware/auth.rs:255-270`
-- **描述**:`org_condition` 函数已定义但仅在 `charge_records.rs` 和 `device_logs.rs` 中使用。`cabinet_tree.rs`、`energy_stats.rs` 等模块自行实现了类似的组织过滤逻辑,代码重复。
-- **建议**:统一使用 `org_condition` 和 `org_condition_for_logs` 进行组织数据隔离,减少重复代码。
-
-### 警告7:H5 用户端 API 端点后端未实现
-- **位置**:`software/web/src/h5/api.ts`
-- **描述**:H5 API 调用了 `/h5/auth/login`、`/h5/dashboard`、`/h5/projects` 等端点,但后端路由中未注册这些路径。H5 用户端目前无法正常工作。
-- **建议**:明确 H5 后端 API 的开发计划,或在 H5 代码中标注为 WIP(Work In Progress)。
-
-### 警告8:Dashboard 页面为占位符
-- **位置**:`software/web/src/pages/Dashboard.tsx`
-- **描述**:后台管理首页仅显示欢迎文字,无任何数据展示。能耗管理页面已有总览数据,Dashboard 应复用或引用。
-- **建议**:实现 Dashboard 数据展示(如今日充电次数、在线设备数、告警数等),可复用 `energy-stats/summary` 接口。
-
-## 建议(可优化)
-
-### 建议1:LIKE 查询中 `%` 通配符未转义
-- **位置**:`software/server/src/routes/users.rs:111`
-- **描述**:`let like_pattern = format!("%{}%", keyword)` 未对关键字中的 `%` 和 `_` 进行转义,用户输入这些字符时可能产生非预期的模糊匹配。
-- **建议**:对 keyword 中的 `%` 和 `_` 进行转义后再拼接 LIKE 模式。
-
-### 建议2:连接池最大连接数可配置化
-- **位置**:`software/server/src/db/mod.rs:11`
-- **描述**:MySQL 连接池 `max_connections(20)` 硬编码。
-- **建议**:通过环境变量 `DB_MAX_CONNECTIONS` 配置,便于不同环境调整。
-
-### 建议3:充电曲线使用静态模拟数据
-- **位置**:`software/web/src/h5/pages/compartment-detail.tsx:174`
-- **描述**:通道详情页的充电曲线使用硬编码数据 `[30, 45, 55, ...]` 展示,非真实数据。
-- **建议**:标注为 TODO 或待后端提供充电曲线 API 后替换,当前可接受。
-
-### 建议4:`log_operation` 函数可抽取为公共模块
-- **位置**:`software/server/src/routes/users.rs:63-85`
-- **描述**:操作日志记录函数 `log_operation` 定义在 `users.rs` 中,但其他模块(如 `roles.rs`、`cabinets.rs`)也需要记录操作日志。
-- **建议**:将 `log_operation` 抽取到公共模块(如 `middleware/auth.rs` 或新建 `utils/log.rs`),供所有路由模块复用。
-
-### 建议5:前端 `eslint-disable` 注释
-- **位置**:`software/web/src/h5/pages/devices.tsx:62`、`device-detail.tsx:37`、`compartment-detail.tsx:43`
-- **描述**:H5 页面中有多处 `eslint-disable-next-line react-hooks/exhaustive-deps`,跳过了 Hook 依赖检查。
-- **建议**:补充正确的依赖项,或使用 `useCallback` 包裹函数以明确依赖关系。
-
-### 建议6:数据库迁移缺少索引定义
-- **位置**:`software/server/src/db/migrate.rs`
-- **描述**:建表语句中未创建索引(除 `energy_stats` 的联合唯一键外)。`charge_records`、`device_logs`、`operation_logs` 等高频查询表缺少对 `cabinet_id`、`created_at` 等常用过滤字段的索引。
-- **建议**:在迁移脚本中添加索引,如:
- - `charge_records`: `cabinet_id`, `start_time`
- - `device_logs`: `cabinet_id`, `created_at`
- - `operation_logs`: `user_id`, `created_at`
- - `cabinets`: `project_id`
-
-## 审查清单完成情况
-
-### 1. 编译与类型检查
-- [x] `cargo check` 零报错零警告
-- [x] `cargo clippy` 零报错零警告
-- [x] `tsc --noEmit` 零报错零警告
-- [x] `vite build` 成功(有 chunk 大小警告)
-
-### 2. 代码规范
-- [x] 单函数基本 <=80 行(少数函数略超,如 `list_users` 约 160 行,可拆分)
-- [x] 命名语义化,无无意义缩写
-- [x] 完整注释(模块级文档注释 + 函数文档注释)
-- [x] 常量抽离(如 `COST_PER_KWH`、`SIGN_START`、`TIMESTAMP_TOLERANCE_SECS`)
-- [x] 无废弃 API 使用
-
-### 3. 错误处理
-- [x] 所有外部 IO/网络请求有异常捕获
-- [x] 无裸 `panic!`(`expect` 仅用于启动阶段的致命错误,如数据库连接失败)
-- [x] 分支逻辑基本全覆盖
-- [x] 错误信息有意义(使用中文描述,含上下文)
-
-### 4. Rust 专项
-- [x] 内存安全,使用 sqlx 连接池无冗余拷贝
-- [x] 无 `unsafe` 块
-- [x] 所有权管理合理(`ConnectionPool` 使用 `Arc>` 共享)
-- [x] 异步代码无阻塞操作
-
-### 5. React 专项
-- [x] 函数式组件 + Hooks,无 Class 组件
-- [x] 状态管理使用 Zustand,分层清晰
-- [x] useEffect 有清理函数(如 DownloadCenter 的 setInterval)
-- [ ] 少量 `as unknown as` 类型断言(见警告4)
-
-### 6. 安全
-- [x] SQL 注入防护(全部使用 `?` 参数化查询)
-- [x] 密码哈希使用 argon2
-- [x] JWT 密钥有环境变量覆盖机制 + 警告
-- [x] 输入参数校验(手机号 11 位、IMEI 15 位等)
-- [ ] 权限校验未覆盖所有 API 接口(见严重问题 1、2)
-- [ ] 数据隔离部分缺失(见严重问题 2)
-- [ ] 敏感参数脱敏不足(见严重问题 4)
-
-### 7. 业务逻辑
-- [x] TCP 协议解析正确(签名验证、LF 粘包处理、超时清理)
-- [x] 权限模型正确(总管理员/企业管理员/普通用户三级)
-- [x] 异步导出流程完整(任务创建 -> 后台处理 -> 下载中心)
-- [x] 前端权限组件正确隐藏/禁用按钮
-
-### 8. 架构
-- [x] 接口层、业务逻辑层、数据模型层分离
-- [x] 无循环依赖
-- [x] 模块职责清晰
-- [x] 路由注册完整
-
-### 9. 性能
-- [ ] 数据库查询缺少索引(见建议 6)
-- [ ] 存在 N+1 查询(见警告 2、3)
-- [x] 前端列表有分页
-- [x] 大数据量导出异步处理
-
-### 10. 可维护性
-- [x] 文件长度合理(最长 467 行 energy/index.tsx)
-- [x] 组件拆分合理
-- [x] 代码重复度低(组织过滤逻辑有重复,见警告 6)
-- [x] 配置外部化(环境变量 + dotenvy)
-
-## 优点
-
-1. **架构清晰**:后端按功能域拆分模块(auth、organizations、cabinets、users 等),每个文件职责单一,不超过 500 行。
-2. **安全性基础扎实**:密码使用 argon2 哈希、JWT 有过期时间、SQL 全部参数化、TCP 签名验证含时间窗口校验。
-3. **RBAC 权限模型完整**:权限码精确到按钮级别,前端 `Permission` 组件支持 hidden/disabled 两种模式,总管理员自动拥有全部权限。
-4. **TCP 协议处理健壮**:使用 `LinesCodec` 处理粘包、`mpsc` 通道分离读写、心跳超时自动清理、连接池 `Arc` 支持并发。
-5. **异步导出设计合理**:download_tasks 表管理任务状态、tokio::spawn 后台生成 Excel、前端轮询刷新、过期文件清理。
-6. **前端工程化规范**:Zustand 状态管理简洁、API 层统一封装、路由守卫完善、H5 端与后台管理端独立认证。
-7. **代码注释充分**:每个模块有模块级文档注释,关键函数有入参/返回值说明,复杂逻辑有行内注释。
-8. **数据库迁移自动化**:启动时自动建表 + 初始化权限码和角色,部署友好。
-
-## 总结
-
-项目整体质量良好,代码规范、架构清晰、安全基础扎实。**优先修复建议**:
-
-1. **P0(立即修复)**:设备指令下发接口添加权限校验(严重问题 1)、组织树接口添加权限校验和数据隔离(严重问题 2)。
-2. **P1(尽快修复)**:前端 restore 后加载用户信息(严重问题 5)、auth_str 日志脱敏(严重问题 4)、默认密码安全化(严重问题 3)。
-3. **P2(迭代优化)**:N+1 查询优化、数据库索引添加、前端代码分割、下载文件端点实现、Dashboard 数据填充。
-4. **P3(后续完善)**:H5 后端 API 开发、操作日志函数抽取公共模块、LIKE 通配符转义。
diff --git a/tasks/review-full-codebase.md b/tasks/review-full-codebase.md
deleted file mode 100644
index f73742a..0000000
--- a/tasks/review-full-codebase.md
+++ /dev/null
@@ -1,184 +0,0 @@
-# 全面代码审查任务
-
-## 目标
-
-全面审查充电柜PMS项目的所有代码,确保质量、安全、规范。
-
-## 审查范围
-
-### 后端 (Rust + Axum)
-`software/server/src/` 目录下所有文件:
-- `main.rs` — 启动入口
-- `config.rs` — 配置管理
-- `error.rs` — 错误处理
-- `db/mod.rs` — 数据库连接
-- `db/migrate.rs` — 数据库迁移
-- `redis.rs` — Redis连接
-- `middleware/auth.rs` — JWT认证+RBAC权限
-- `routes/` — 所有路由模块
- - `auth.rs` — 登录/登出
- - `organizations.rs` — 组织CRUD
- - `projects.rs` — 项目CRUD
- - `cabinets.rs` — 设备CRUD
- - `cabinet_tree.rs` — 树状结构
- - `users.rs` — 用户管理
- - `roles.rs` — 角色管理
- - `users_perm.rs` — 用户权限
- - `charge_records.rs` — 充电记录
- - `device_logs.rs` — 设备日志
- - `energy_stats.rs` — 能耗统计
- - `downloads.rs` — 下载中心
- - `operation_logs.rs` — 操作日志
-- `tcp/` — TCP通讯服务
- - `server.rs` — TCP监听
- - `connection.rs` — 连接管理
- - `protocol.rs` — 协议解析
- - `handler.rs` — 消息处理
- - `commands.rs` — 指令下发
-
-### 前端 (React + TypeScript)
-`software/web/src/` 目录下所有文件:
-- `App.tsx` — 路由配置
-- `main.tsx` — 入口
-- `layouts/AdminLayout.tsx` — 后台布局
-- `api/` — API封装
-- `stores/auth.ts` — 认证状态
-- `utils/permission.ts` — 权限工具
-- `components/Permission.tsx` — 权限组件
-- `pages/` — 所有页面
- - `Login.tsx` — 登录页
- - `Dashboard.tsx` — 首页
- - `devices/` — 设备管理
- - `charge-records/` — 充电记录
- - `device-logs/` — 设备日志
- - `energy/` — 能耗管理
- - `settings/` — 系统设置
-- `h5/` — H5用户端
- - `pages/` — H5页面
- - `components.tsx` — 移动端组件
- - `layout.tsx` — H5布局
-
-## 审查清单
-
-### 1. 编译与类型检查
-- [ ] `cargo check` 零报错零警告
-- [ ] `cargo clippy` 零报错零警告
-- [ ] `tsc --noEmit` 零报错零警告
-- [ ] `vite build` 成功
-
-### 2. 代码规范
-- [ ] 单函数≤80行
-- [ ] 命名语义化,无无意义缩写
-- [ ] 完整注释(入参、返回值、复杂逻辑)
-- [ ] 常量抽离,无硬编码散落
-- [ ] 无废弃API使用
-
-### 3. 错误处理
-- [ ] 所有外部IO/网络请求有异常捕获
-- [ ] 无裸 `panic!` 或 `unwrap()`
-- [ ] 分支逻辑全覆盖,无遗漏兜底
-- [ ] 错误信息有意义,便于调试
-
-### 4. Rust专项
-- [ ] 内存安全,无冗余拷贝
-- [ ] 无 `unsafe` 块(除非有合理理由)
-- [ ] 所有权管理合理
-- [ ] 异步代码无阻塞操作
-
-### 5. React专项
-- [ ] 函数式组件+Hooks,无Class组件
-- [ ] 状态分层管理(接口/渲染/业务逻辑)
-- [ ] 无内存泄漏(useEffect清理)
-- [ ] 无 `as any` 类型断言
-
-### 6. 安全
-- [ ] SQL注入防护(全部参数化查询)
-- [ ] 密码哈希(argon2/bcrypt)
-- [ ] JWT密钥安全(非默认值)
-- [ ] 输入参数校验
-- [ ] 权限校验覆盖所有API接口
-- [ ] 数据隔离(组织/项目/设备级)
-- [ ] 敏感参数脱敏(日志中不打印密码/token)
-
-### 7. 业务逻辑
-- [ ] TCP协议解析正确(签名验证、粘包处理、超时清理)
-- [ ] 权限模型正确(总管理员/企业管理员/普通用户)
-- [ ] 异步导出流程完整(任务创建→后台处理→下载)
-- [ ] 前端权限组件正确隐藏/显示按钮
-
-### 8. 架构
-- [ ] 接口层、业务逻辑层、数据模型层分离
-- [ ] 无循环依赖
-- [ ] 模块职责清晰
-- [ ] 路由注册完整,无遗漏
-
-### 9. 性能
-- [ ] 数据库查询有索引
-- [ ] 无N+1查询问题
-- [ ] 前端列表有分页
-- [ ] 大数据量导出异步处理
-
-### 10. 可维护性
-- [ ] 文件长度合理(≤300行)
-- [ ] 组件拆分合理
-- [ ] 代码重复度低
-- [ ] 配置外部化(环境变量)
-
-## 输出格式
-
-```markdown
-# 全面代码审查报告
-
-## 总体评价
-[整体质量评分 1-10分,简要评价]
-
-## 统计
-- 后端文件数:X
-- 前端文件数:X
-- 总代码行数:X
-- 问题总数:X(严重X / 警告X / 建议X)
-
-## 严重问题( 必须修复)
-### 问题1:[标题]
-- **位置**:`文件路径:行号`
-- **描述**:[问题描述]
-- **风险**:[可能的影响]
-- **建议**:[修复方案]
-
-## 警告(🟡 建议修复)
-### 警告1:[标题]
-- **位置**:`文件路径:行号`
-- **描述**:[问题描述]
-- **建议**:[改进方案]
-
-## 优点
-- [值得肯定的设计/实现]
-
-## 总结
-[总体结论和优先修复建议]
-```
-
-## 审查工具
-
-```bash
-# 后端静态检查
-cd software/server && cargo check && cargo clippy
-
-# 前端类型检查
-cd software/web && tsc --noEmit
-
-# 前端构建测试
-cd software/web && vite build
-
-# 代码行数统计
-find src -name "*.rs" -exec wc -l {} + | sort -n
-find src -name "*.tsx" -exec wc -l {} + | sort -n
-```
-
-## 质量约束
-
-1. 审查必须覆盖所有清单项目
-2. 每个问题必须给出具体位置和修复建议
-3. 严重问题必须标注风险等级
-4. 审查报告用中文撰写
-5. 必须实际读取代码文件,不能仅凭文件名判断
diff --git a/tasks/review-mimo-report.md b/tasks/review-mimo-report.md
deleted file mode 100644
index 40d0580..0000000
--- a/tasks/review-mimo-report.md
+++ /dev/null
@@ -1,262 +0,0 @@
-# 全面代码审查报告
-
-## 总体评价
-
-**评分:7.5 / 10**
-
-项目整体架构清晰,模块拆分合理,代码质量较好。后端采用 Rust + Axum 技术栈,安全性较高;前端 React + Arco Design 组件化开发规范。权限模型(RBAC + 数据隔离)设计完善,TCP 通讯协议实现完整。主要问题集中在:部分编译错误风险、H5 后端接口缺失、文件下载端点未实现、以及若干安全/性能细节需改进。
-
-## 统计
-
-- 后端文件数:29(Rust)
-- 前端文件数:36(TypeScript/React)
-- 总代码行数:约 6,800 行(后端约 3,500 行,前端约 3,300 行)
-- 问题总数:23(严重 5 / 警告 10 / 建议 8)
-
-## 审查清单完成情况
-
-### 1. 编译与类型检查
-- [ ] `cargo check` — 未能执行(环境中无 Rust 工具链),**代码审查发现潜在编译问题**(见严重问题 #1、#2)
-- [ ] `cargo clippy` — 未能执行
-- [ ] `tsc --noEmit` — 未能执行
-- [ ] `vite build` — 未能执行
-
-### 2. 代码规范
-- [x] 单函数 ≤ 80 行 — 大部分符合,`users.rs::list_users` 约 160 行超标
-- [x] 命名语义化 — 整体良好,无无意义缩写
-- [x] 完整注释 — Rust 文档注释完善,前端 JSDoc 基本覆盖
-- [x] 常量抽离 — `COST_PER_KWH`、`JWT_SECRET_DEFAULT` 等已抽离
-- [x] 无废弃 API — 未发现
-
-### 3. 错误处理
-- [x] 所有外部 IO/网络请求有异常捕获 — 全面覆盖
-- [x] 无裸 `panic!` 或 `unwrap()` — `expect()` 仅用于启动阶段关键操作
-- [x] 分支逻辑全覆盖 — match 语句均有兜底分支
-- [x] 错误信息有意义 — 中文错误信息便于调试
-
-### 4. Rust 专项
-- [x] 内存安全,无冗余拷贝 — 合理使用引用和 Clone
-- [x] 无 `unsafe` 块 — 确认无
-- [x] 所有权管理合理 — ConnectionPool 使用 Arc> 模式
-- [x] 异步代码无阻塞操作 — 确认无阻塞调用
-
-### 5. React 专项
-- [x] 函数式组件 + Hooks — 全部为函数式组件
-- [x] 状态分层管理 — Zustand 管理认证状态,页面级 useState
-- [x] 无内存泄漏 — useEffect 清理函数正确(DownloadCenter、compartment-detail)
-- [ ] 无 `as any` 类型断言 — 存在 `as never`、`as unknown as` 类型断言
-
-### 6. 安全
-- [x] SQL 注入防护 — 全部使用参数化查询 `?` 占位符
-- [x] 密码哈希 — argon2(行业领先)
-- [x] JWT 密钥安全 — 支持环境变量,启动时校验默认值告警
-- [x] 输入参数校验 — 手机号、IMEI、角色值等均有校验
-- [x] 权限校验覆盖 — 所有 API 接口均有 `check_permission`
-- [x] 数据隔离 — `org_condition` / `org_condition_for_logs` 实现组织级过滤
-- [ ] 敏感参数脱敏 — `auth_str` 在 API 响应中明文返回
-
-### 7. 业务逻辑
-- [x] TCP 协议解析正确 — 签名验证、LF 粘包处理、超时清理均已实现
-- [x] 权限模型正确 — 总管理员/企业管理员/普通用户三级
-- [x] 异步导出流程完整 — 任务创建 → tokio 后台处理 → 下载中心查询
-- [x] 前端权限组件正确 — Permission 组件支持 hidden/disabled 两种模式
-
-### 8. 架构
-- [x] 接口层、业务逻辑层、数据模型层分离 — routes/ 处理请求,handler/ 处理业务逻辑
-- [x] 无循环依赖 — 模块间单向依赖
-- [x] 模块职责清晰 — 按功能域拆分(organizations/projects/cabinets 等)
-- [x] 路由注册完整 — `routes/mod.rs` 聚合所有子路由
-
-### 9. 性能
-- [ ] 数据库查询有索引 — 迁移脚本缺少关键索引(见警告 #6)
-- [x] 无 N+1 查询问题 — 柜子详情的仓板/仓体查询在可接受范围(N≤6)
-- [x] 前端列表有分页 — 所有列表页均实现分页
-- [x] 大数据量导出异步处理 — tokio::spawn 后台生成 Excel
-
-### 10. 可维护性
-- [ ] 文件长度合理(≤ 300 行)— `users.rs`(553 行)、`energy/index.tsx`(468 行)超标
-- [x] 组件拆分合理 — OrganizationTree、CabinetGrid、DownloadCenter 等独立组件
-- [ ] 代码重复度低 — 导出逻辑、状态映射存在重复
-- [x] 配置外部化 — 环境变量 + `.env` 文件
-
----
-
-## 严重问题(必须修复)
-
-### 问题 1:`list_users` 查询列数与解构不匹配
-- **位置**:`software/server/src/routes/users.rs:158`
-- **描述**:`query_as` 查询返回 7 列 `(id, phone, name, role, organization_id, status, created_at)`,但行后 `UpdateUserInput` 结构体定义出现在文件中间(第 41 行),且 `query_as` 没有对应的 7 元素元组类型声明。第 158 行的类型注解为 `Vec<(i64, String, Option, i32, Option, i32, String)>`(7 元素),但此处 `query_as` 调用没有使用 `FromRow` 派生结构体,而是直接使用元组。sqlx 对元组元素数量有上限(通常支持到 6 个),7 元素元组可能导致编译错误。
-- **风险**:编译失败,无法构建后端服务
-- **建议**:定义 `#[derive(sqlx::FromRow)]` 结构体替代元组,或将查询拆分为两步
-
-### 问题 2:充电记录 COUNT 查询 JOIN 表不匹配
-- **位置**:`software/server/src/routes/charge_records.rs:187-193`
-- **描述**:计数 SQL 为 `SELECT COUNT(*) FROM charge_records cr LEFT JOIN cabin_boards cb ON cr.compartment_id = cb.id`,但 WHERE 条件中引用了 `cb.id`(期望是 cabin_boards.id)和 `cr.compartment_id`。实际 `fetch_charge_rows` 中 JOIN 了 `compartments comp ON cr.compartment_id = comp.id` 再 JOIN `cabin_boards cb ON comp.cabin_board_id = cb.id`。COUNT 查询缺少 `compartments` 表的 JOIN,导致 `cabin_board_id` 过滤条件无法正确工作。
-- **风险**:按仓板过滤时,分页总数计算错误,前端显示数据不完整
-- **建议**:COUNT 查询使用与列表查询相同的 JOIN 链路:`cr → compartments → cabin_boards`
-
-### 问题 3:H5 后端接口全部缺失
-- **位置**:`software/web/src/h5/api.ts` 全部接口
-- **描述**:H5 用户端调用了 `/h5/auth/login`、`/h5/dashboard`、`/h5/projects`、`/h5/cabinets`、`/h5/cabinets/:id`、`/h5/compartments/:id`、`/h5/charge/start`、`/h5/charge/stop`、`/h5/door/open` 等接口,但后端 `routes/mod.rs` 中未注册任何 `/h5/` 路由。
-- **风险**:H5 用户端完全无法使用,所有请求返回 404
-- **建议**:在后端实现 H5 路由模块,或明确标注为待开发并在前端添加提示
-
-### 问题 4:文件下载端点未实现
-- **位置**:`software/server/src/routes/downloads.rs:162`
-- **描述**:`get_download` 函数返回 `download_url: "/api/downloads/{id}/file"`,但 `routes/mod.rs` 中没有注册该路由。用户点击下载后无法获取文件。
-- **风险**:导出功能不完整,用户无法下载已生成的 Excel 文件
-- **建议**:添加文件下载端点,使用 `axum::response::File` 或 `tower-http::ServeDir` 提供文件服务
-
-### 问题 5:`authenticated` 标志未强制校验
-- **位置**:`software/server/src/tcp/handler.rs:146`
-- **描述**:`DeviceConnection` 有 `authenticated` 字段,`handle_login` 成功后调用 `pool.set_authenticated(dev_id)`。但 `handle_status_post` 中检查的是 `pool.is_online(dev_id)`,该方法仅检查 `authenticated` 标志。然而,在 `server.rs:98-104` 中,设备在发送 `auth_str` 或 `login` 时就被注册到连接池(`pool.register`),此时 `authenticated` 为 `false`。问题在于 `is_online` 返回 `authenticated` 为 `false` 时,`status_post` 确实会拒绝(返回"设备未登录"),但 `auth_str` 请求可以无限次调用而不限流,可能被用于资源耗尽攻击。
-- **风险**:安全码获取接口无频率限制,可能被恶意设备利用
-- **建议**:对 `auth_str` 请求添加频率限制或 IP 限制
-
----
-
-## 警告(建议修复)
-
-### 警告 1:`list_users` 函数过长
-- **位置**:`software/server/src/routes/users.rs:96-256`
-- **描述**:函数约 160 行,包含多种条件分支(有无关键字搜索 x 有无组织过滤),大量重复的 SQL 构建逻辑。
-- **建议**:拆分为 `build_user_count_query` 和 `build_user_list_query` 辅助函数,或使用动态 SQL 构建器
-
-### 警告 2:能耗导出缺少组织数据隔离
-- **位置**:`software/server/src/routes/energy_stats.rs:460-553`
-- **描述**:`generate_energy_excel` 后台任务中使用 `build_export_clause` 构建查询条件,但未追加组织过滤条件。企业管理员可能导出全部组织的能耗数据。
-- **建议**:在导出查询中追加 `org_condition` 过滤
-
-### 警告 3:前端多处 `as unknown as` / `as never` 类型断言
-- **位置**:`OrganizationTree.tsx:169`、`devices/index.tsx:81,103`、`AddCabinetModal.tsx:44`
-- **描述**:使用 `as never`、`as unknown as` 绕过 TypeScript 类型检查,掩盖了 API 响应类型与实际数据的不匹配。
-- **建议**:修正 API 泛型参数类型,使响应类型与 Arco Tree/Table 组件期望的类型一致
-
-### 警告 4:数据库迁移缺少索引
-- **位置**:`software/server/src/db/migrate.rs`
-- **描述**:以下高频查询列缺少索引:
- - `charge_records.cabinet_id`、`charge_records.start_time`
- - `device_logs.cabinet_id`、`device_logs.created_at`
- - `operation_logs.user_id`、`operation_logs.created_at`
- - `users.organization_id`、`cabinets.project_id`
-- **建议**:在迁移脚本中为上述列添加 `INDEX`
-
-### 警告 5:导出逻辑大量重复
-- **位置**:`charge_records.rs:307-395` 与 `energy_stats.rs:460-553`
-- **描述**:两个导出函数的结构几乎相同:更新状态 → 获取参数 → 查询数据 → 更新进度 → 生成 Excel → 保存文件 → 更新完成状态。
-- **建议**:抽取通用的 `run_export_task` 泛型函数,传入数据查询和 Excel 生成闭包
-
-### 警告 6:H5 认证恢复无服务端校验
-- **位置**:`software/web/src/h5/auth.ts:48-55`
-- **描述**:`restore()` 从 localStorage 读取 token 后直接设置 `isAuthenticated: true`,不验证 token 有效性。与后台管理端的 `restore`(调用 `/auth/me` 验证)行为不一致。
-- **建议**:H5 端也添加服务端 token 校验(需先实现 H5 后端接口)
-
-### 警告 7:`regenerate_auth` 返回明文安全码
-- **位置**:`software/server/src/routes/cabinets.rs:232-255`
-- **描述**:重新生成安全码后通过 API 响应返回明文 `auth_str`。虽然管理员需要知道新安全码以配置设备,但这增加了泄露风险。
-- **建议**:考虑仅显示一次或添加操作确认,并在操作日志中记录
-
-### 警告 8:`roles.rs::list_roles` 存在 N+1 查询
-- **位置**:`software/server/src/routes/roles.rs:36-72`
-- **描述**:先查询所有角色,再为每个角色单独查询权限码。角色数量通常为个位数,当前可接受,但不够优雅。
-- **建议**:使用单条 SQL 通过 `GROUP_CONCAT` 或 `JOIN` 一次性获取角色及其权限码
-
-### 警告 9:导出文件路径使用相对路径
-- **位置**:`charge_records.rs:378`、`energy_stats.rs:536`
-- **描述**:`let file_dir = "./exports"` 使用相对路径,依赖进程工作目录。
-- **建议**:使用配置项或绝对路径(如 `cfg.export_dir`)
-
-### 警告 10:清理下载接口缺少用户权限校验
-- **位置**:`software/server/src/routes/downloads.rs:176-220`
-- **描述**:`cleanup_downloads` 清理所有过期任务的文件,不区分用户。虽然有 `charge_record:export` 权限检查,但普通用户不应能清理其他用户的文件。
-- **建议**:添加 `WHERE user_id = ?` 过滤,或限制为总管理员操作
-
----
-
-## 建议(可优化)
-
-### 建议 1:Dashboard 页面为空壳
-- **位置**:`software/web/src/pages/Dashboard.tsx`
-- **描述**:仅显示欢迎文字,无实际数据展示。
-- **建议**:可复用能耗总览数据或设备在线统计,提供有价值的首页视图
-
-### 建议 2:状态映射常量重复定义
-- **位置**:`cabinet.tsx:24-37`、`CabinetGrid.tsx:22-27`、`charge-records/index.tsx:52-56`
-- **描述**:柜子状态和仓体状态的映射在多个文件中重复定义。
-- **建议**:抽取到 `constants/status.ts` 统一管理
-
-### 建议 3:`devices/index.tsx` 状态变量过多
-- **位置**:`software/web/src/pages/devices/index.tsx`
-- **描述**:组件内有 15+ 个 `useState`,管理弹窗、编辑、右键菜单等多种状态。
-- **建议**:使用 `useReducer` 或拆分为子组件(如 `useOrgModal`、`useContextMenu`)
-
-### 建议 4:`seed_permissions` 可批量插入
-- **位置**:`software/server/src/db/migrate.rs:186-223`
-- **描述**:逐条 `INSERT IGNORE` 21 个权限码,可优化为单条批量 INSERT。
-- **建议**:使用 `INSERT IGNORE INTO permissions (code, name) VALUES (?, ?), (?, ?), ...` 批量插入
-
-### 建议 5:电费单价应可配置
-- **位置**:`software/server/src/routes/energy_stats.rs:15`
-- **描述**:`COST_PER_KWH` 硬编码为 0.80,不同地区/时段电价不同。
-- **建议**:移至配置文件或数据库配置表
-
-### 建议 6:`energy/index.tsx` 文件过长
-- **位置**:`software/web/src/pages/energy/index.tsx`(468 行)
-- **描述**:包含总览卡片、排行表格、项目统计、趋势图表等全部逻辑。
-- **建议**:拆分为 `SummaryCards`、`CabinetRanking`、`ProjectStats`、`TrendChart` 子组件
-
-### 建议 7:H5 `routes.tsx` 导入顺序不规范
-- **位置**:`software/web/src/h5/routes.tsx:47-50`
-- **描述**:`ScanRedirect` 组件的 `import` 语句出现在文件中间(第 47 行),违反 ES Module 导入应在文件顶部的约定。
-- **建议**:将所有 import 移到文件顶部
-
-### 建议 8:充电曲线使用硬编码模拟数据
-- **位置**:`software/web/src/h5/pages/compartment-detail.tsx:173-175`
-- **描述**:充电曲线图表使用固定数组 `[30, 45, 55, ...]` 模拟,不是真实数据。
-- **建议**:标注为 TODO 或待后端提供充电曲线 API 后替换
-
----
-
-## 优点
-
-1. **架构设计清晰**:后端按功能域拆分模块(organizations/projects/cabinets/users/roles 等),路由注册集中管理,模块职责单一
-2. **安全性基础扎实**:argon2 密码哈希、JWT 认证、RBAC 权限模型、参数化 SQL 查询、组织级数据隔离,安全层面考虑全面
-3. **TCP 通讯实现完整**:LF 分隔粘包处理、签名验证(SHA256 + 时间窗口)、心跳超时清理、连接池管理,协议层实现可靠
-4. **异步导出设计合理**:download_tasks 表跟踪任务状态、tokio 后台生成 Excel、前端轮询刷新,完整的异步导出流程
-5. **错误处理规范**:统一 `AppError` 错误类型,所有 IO 操作有异常捕获,无裸 panic,错误信息有意义
-6. **前端组件化好**:Permission 权限组件支持 hidden/disabled 模式,OrganizationTree 递归树组件,DownloadCenter 可复用弹窗
-7. **操作日志完善**:关键操作(创建/编辑/删除用户等)均记录操作日志,便于审计
-8. **H5 移动端体验好**:Tailwind CSS 样式统一,底部导航、确认弹窗、加载/错误状态组件完善
-9. **数据库设计合理**:外键约束、级联删除、UNIQUE 约束、COMMENT 注释,表结构规范
-10. **配置管理灵活**:环境变量 + `.env` 文件,敏感配置有默认值告警
-
----
-
-## 总结
-
-### 优先修复建议(按紧急程度排序)
-
-1. **P0 — 立即修复**:
- - 修复 `list_users` 的 7 元素元组编译问题(严重 #1)
- - 修复充电记录 COUNT 查询 JOIN 缺失(严重 #2)
- - 实现文件下载端点 `/api/downloads/:id/file`(严重 #4)
-
-2. **P1 — 近期完成**:
- - 实现 H5 后端接口模块(严重 #3),或在前端标注为"待开发"
- - 添加数据库关键索引(警告 #4)
- - 修复能耗导出缺少组织过滤(警告 #2)
-
-3. **P2 — 迭代优化**:
- - 拆分 `list_users` 函数(警告 #1)
- - 消除前端类型断言(警告 #3)
- - 统一导出逻辑减少重复(警告 #5)
- - 导出文件路径配置化(警告 #9)
-
-4. **P3 — 持续改进**:
- - Dashboard 首页数据展示(建议 #1)
- - 状态映射常量统一(建议 #2)
- - 电费单价可配置化(建议 #5)
-
-### 结论
-
-项目代码整体质量 **良好**,架构设计规范,安全和错误处理基础扎实。主要风险点在于:部分代码可能存在编译问题需要验证、H5 后端接口完全缺失、文件下载端点未实现。建议优先修复 P0 级问题后进行 `cargo check` 和 `tsc --noEmit` 验证,确保代码可正常编译。
diff --git a/tasks/review-nemotron-report.md b/tasks/review-nemotron-report.md
deleted file mode 100644
index 8cee2a4..0000000
--- a/tasks/review-nemotron-report.md
+++ /dev/null
@@ -1,296 +0,0 @@
-# 全面代码审查报告
-
-## 总体评价
-
-**质量评分:7/10**
-
-项目整体架构清晰,模块划分合理,代码风格统一。后端采用 Rust + Axum 技术栈,安全性较好(参数化查询、Argon2 密码哈希、JWT 认证)。前端 React + Arco Design 组件库使用规范,H5 移动端独立实现。但存在若干权限码缺失、数据隔离不完整、前后端接口字段不匹配等严重问题需优先修复。
-
-## 统计
-
-- 后端文件数:29(Rust)
-- 前端文件数:34(TypeScript/React)
-- 后端代码行数:6,114
-- 前端代码行数:5,300
-- 总代码行数:11,414
-- 问题总数:24(严重 7 / 警告 9 / 建议 8)
-
----
-
-## 严重问题(必须修复)
-
-### 问题1:权限码种子数据缺失组织/项目管理权限
-
-- **位置**:`software/server/src/db/migrate.rs:187-209` + `software/server/src/routes/organizations.rs:154,172,198,227` + `software/server/src/routes/projects.rs:20,40,78,107`
-- **描述**:`seed_permissions` 函数只初始化了 `device:*`、`charge:*`、`charge_record:*`、`device_log:*`、`energy:*`、`user:*`、`role:*`、`operation_log:*` 等权限码,但组织管理 API 检查的是 `org:view`/`org:create`/`org:edit`/`org:delete`,项目管理 API 检查的是 `project:view`/`project:create`/`project:edit`/`project:delete`。这些权限码从未在数据库中创建。
-- **风险**:非总管理员用户(role_level < 2)永远无法执行组织和项目的增删改查操作,因为 `check_permission` 在权限码集合中找不到这些 code 会返回 Forbidden。
-- **建议**:在 `seed_permissions` 中补充以下权限码:
- ```
- ("org:view", "查看组织"), ("org:create", "创建组织"),
- ("org:edit", "编辑组织"), ("org:delete", "删除组织"),
- ("project:view", "查看项目"), ("project:create", "创建项目"),
- ("project:edit", "编辑项目"), ("project:delete", "删除项目"),
- ```
- 并为 `org_admin` 角色分配 `org:view`、`project:view`、`project:create`、`project:edit` 权限。
-
-### 问题2:设备管理缺少 `device:edit` 权限码
-
-- **位置**:`software/server/src/db/migrate.rs:188` + `software/server/src/routes/cabinets.rs:166,237`
-- **描述**:`cabinets.rs` 中 `update_cabinet` 和 `regenerate_auth` 检查 `device:edit` 权限,但种子数据中只有 `device:view`、`device:operate`、`device:create`、`device:delete`,没有 `device:edit`。
-- **风险**:非总管理员用户无法编辑柜子或重新生成安全码。
-- **建议**:在种子数据中添加 `("device:edit", "编辑设备")`。
-
-### 问题3:H5 接口缺少数据隔离 — 仪表盘返回全局数据
-
-- **位置**:`software/server/src/routes/h5.rs:157-245`
-- **描述**:`get_dashboard` 接口查询的在线柜子数、充电中仓体数、空闲仓体数、故障仓体数、今日充电次数/充电量均为全局统计,未按用户所属组织过滤。企业管理员和普通用户可以看到全平台数据。
-- **风险**:数据泄露,违反多租户隔离原则。
-- **建议**:所有统计查询追加 `organization_id` 过滤条件,与后台管理接口保持一致的隔离策略。
-
-### 问题4:H5 设备详情和仓体详情缺少权限校验
-
-- **位置**:`software/server/src/routes/h5.rs:420-488,558-634`
-- **描述**:`get_cabinet_detail`、`get_cabinet_by_abstract_id`、`get_compartment_detail` 三个接口没有进行任何权限检查(只有 `let _ = &user;`),任意已登录用户可通过遍历 ID 访问任意设备和仓体数据。
-- **风险**:未授权数据访问,任意用户可查看不属于自己组织的设备。
-- **建议**:添加组织隔离校验,确保用户只能访问本组织下的设备。
-
-### 问题5:前后端 H5 告警数据结构不匹配
-
-- **位置**:`software/server/src/routes/h5.rs:206-224` vs `software/web/src/h5/api.ts:21-26` + `software/web/src/h5/components.tsx:96-113`
-- **描述**:后端返回告警字段为 `{ compartment_id, channel, cabinet_id, alert_type }`,前端 `AlertItem` 接口定义为 `{ project: string, cabinet: string, channel: number, alert_type: string }`。`AlertCard` 组件渲染 `project` 和 `cabinet` 字段,但后端未返回这两个字段,前端会显示 `undefined`。
-- **风险**:H5 首页告警通知显示异常,用户看到 `undefined - undefined 通道X`。
-- **建议**:后端补充 `project` 和 `cabinet` 字段(通过 JOIN 查询),或前端改为根据 ID 显示。
-
-### 问题6:SQL 字符串拼接中直接内联 org_id(h5.rs)
-
-- **位置**:`software/server/src/routes/h5.rs:744-756`
-- **描述**:`org_project_condition` 函数使用 `format!` 将 `org_id` 直接拼入 SQL 字符串(`"...WHERE organization_id = {}"`),而非使用参数化查询的 `?` 占位符。虽然 `org_id` 来自 JWT 解析的 `i64` 类型(理论上安全),但这种模式违反了参数化查询的最佳实践。
-- **风险**:代码模式不安全,若未来重构改变了 `org_id` 的来源类型,可能引入 SQL 注入。
-- **建议**:改为返回参数化条件,使用 `?` 占位符并通过 `.bind()` 传值,与 `auth::org_condition` 保持一致。
-
-### 问题7:下载中心权限码检查不精确
-
-- **位置**:`software/server/src/routes/downloads.rs:96,141,184,241`
-- **描述**:下载中心所有接口(列表、详情、文件下载、清理)统一检查 `charge_record:export` 权限。但能耗统计导出也使用下载中心,应同时接受 `energy:export` 权限。当前设计导致只有充电记录导出权限的用户才能看到所有下载任务。
-- **风险**:权限模型不够灵活,能耗导出权限的用户无法使用下载中心。
-- **建议**:下载中心列表接口改为检查 `charge_record:export` 或 `energy:export`(任一即可),或新增 `download:view` 权限码。
-
----
-
-## 警告(建议修复)
-
-### 警告1:`users.rs` 中 `list_users` 查询分支大量重复
-
-- **位置**:`software/server/src/routes/users.rs:143-221`
-- **描述**:`list_users` 函数中,带关键字/不带关键字 x 有组织过滤/无组织过滤,产生了 6 个几乎相同的 SQL 查询分支,代码约 80 行。
-- **建议**:使用动态 SQL 构建(类似 `charge_records.rs` 的 `build_where_clause` 模式),将条件拼接统一处理。
-
-### 警告2:`energy_stats.rs` 组织过滤逻辑重复
-
-- **位置**:`software/server/src/routes/energy_stats.rs:90-99,174-183,230-236,289-298`
-- **描述**:`get_summary`、`get_cabinet_ranking`、`get_project_stats`、`get_trend` 四个函数各自独立实现组织过滤逻辑,代码重复。
-- **建议**:抽取为通用函数(类似 `auth::org_condition`),统一复用。
-
-### 警告3:`list_cabinet_options` 无组织过滤
-
-- **位置**:`software/server/src/routes/cabinets.rs:366-389`
-- **描述**:当 `project_id` 为空时,`list_cabinet_options` 返回所有柜子,未按用户组织过滤。企业管理员和普通用户可看到全部柜子下拉选项。
-- **建议**:追加 `auth::org_condition` 过滤。
-
-### 警告4:设备重连时旧连接未显式关闭
-
-- **位置**:`software/server/src/tcp/connection.rs:49-52` + `software/server/src/tcp/server.rs:98-104`
-- **描述**:`ConnectionPool::register` 直接 `insert` 覆盖旧连接,但旧连接的 `mpsc::UnboundedSender` 克隆体在 `handle_connection` 中仍被持有,旧 TCP 连接的任务不会立即终止。
-- **建议**:注册时检查是否已存在旧连接,若有则通过通道发送关闭信号或记录日志。
-
-### 警告5:前端 `as unknown as` 类型断言绕过类型安全
-
-- **位置**:`software/web/src/pages/devices/index.tsx:81,103` + `software/web/src/pages/devices/AddCabinetModal.tsx:44`
-- **描述**:多处使用 `(res as unknown as TreeNode[])` 双重类型断言,绕过了 TypeScript 类型检查。说明 `api.get` 的返回类型 `ApiResponse` 与实际使用不匹配。
-- **建议**:检查 `api.get` 泛型推导是否正确,修复类型定义使其无需强制转换。
-
-### 警告6:JWT 密钥常量重复定义
-
-- **位置**:`software/server/src/config.rs:4` vs `software/server/src/middleware/auth.rs:33`
-- **描述**:`JWT_SECRET_DEFAULT` 在 `config.rs` 和 `middleware/auth.rs` 中各定义一次,值相同但独立维护。
-- **建议**:只在 `config.rs` 中定义一次,`middleware/auth.rs` 引用 `crate::config` 中的常量,或在 `AppState` 中传递已解析的密钥。
-
-### 警告7:401 响应未自动跳转登录页
-
-- **位置**:`software/web/src/api/request.ts:44-59`
-- **描述**:`request` 函数在收到 401 响应时抛出 `ApiError`,但未自动清除本地 token 或跳转到登录页。当 JWT 过期后,用户操作会报错但不会自动跳转到登录页。
-- **建议**:在 `request` 函数中拦截 401 状态码,自动清除 localStorage 并跳转到 `/login`。
-
-### 警告8:数据库表缺少关键索引
-
-- **位置**:`software/server/src/db/migrate.rs`
-- **描述**:多个高频查询涉及的列缺少索引:
- - `cabin_boards.cabinet_id`(设备详情查询)
- - `compartments.cabin_board_id`(仓体列表查询)
- - `charge_records.compartment_id`(充电记录查询)
- - `charge_records.start_time`(时间范围过滤)
- - `device_logs.cabinet_id` + `device_logs.created_at`(日志查询)
- - `download_tasks.user_id`(下载列表查询)
- - `users.organization_id`(用户组织过滤)
- - `cabinets.project_id`(项目下柜子查询)
-- **建议**:为上述列添加索引。MySQL 的 `FOREIGN KEY` 约束会自动创建索引,但需确认迁移脚本中已包含。
-
-### 警告9:`DeviceMessage.extra` 使用 `Option` 与 `flatten` 冗余
-
-- **位置**:`software/server/src/tcp/protocol.rs:29-31`
-- **描述**:`extra: Option` 配合 `#[serde(flatten)]`,当无额外字段时值为 `Value::Null` 而非 `None`,`Option` 包裹无实际意义。
-- **建议**:改为 `extra: Value` 或去掉 `Option`。
-
----
-
-## 建议
-
-### 建议1:`Protocol::to_json` 使用 `expect` 可改为 `unwrap_or_default`
-
-- **位置**:`software/server/src/tcp/protocol.rs:58,80`
-- **描述**:`ServerResponse::to_json` 和 `DeviceCommand::to_json` 使用 `expect` 序列化。虽然理论上不应失败,但为安全起见可改为 `unwrap_or_default()`。
-- **建议**:改为 `serde_json::to_string(self).unwrap_or_default()`。
-
-### 建议2:`Dashboard.tsx` 为空白占位页
-
-- **位置**:`software/web/src/pages/Dashboard.tsx`
-- **描述**:后台管理 Dashboard 页面仅为静态文本 "欢迎使用充电柜管理系统",未展示任何统计数据。
-- **建议**:参考 H5 首页实现,展示设备在线率、今日充电量、告警数量等关键指标。
-
-### 建议3:柜子详情页显示硬编码零值
-
-- **位置**:`software/web/src/pages/devices/cabinet.tsx:144-147`
-- **描述**:柜子详情的仓体卡片中,电压/电流/功率显示为硬编码的 `0.0V`/`0.0A`/`0W`,未从 Redis 实时数据获取。
-- **建议**:通过后端接口获取仓体实时数据并渲染。
-
-### 建议4:`energy_stats.rs` 中 `get_summary` 执行了 4 次独立查询
-
-- **位置**:`software/server/src/routes/energy_stats.rs:82-162`
-- **描述**:`get_summary` 分别执行今日能耗、本月能耗、柜子总数、在线柜子数 4 次查询,可合并为 1-2 次查询减少数据库往返。
-- **建议**:使用 `CASE WHEN` 或子查询合并。
-
-### 建议5:H5 `DownloadCenter` 轮询定时器清理不完整
-
-- **位置**:`software/web/src/pages/charge-records/DownloadCenter.tsx:79-86`
-- **描述**:`useEffect` 依赖 `[visible, tasks, fetchTasks]`,每次 `tasks` 变化都会清除并重建 `setInterval`。当有进行中任务时,每 5 秒触发一次重建。
-- **建议**:将 `tasks` 从依赖数组中移除,改用 `useRef` 跟踪任务状态。
-
-### 建议6:`h5/routes.tsx` 中 import 语句位于文件中间
-
-- **位置**:`software/web/src/h5/routes.tsx:47-50`
-- **描述**:`import { useEffect, useState }` 等导入语句出现在文件中间(第 47 行),违反 ES Module 规范(虽然 TypeScript 编译器允许)。
-- **建议**:将所有 import 语句移至文件顶部。
-
-### 建议7:`config.rs` 中 `validate` 仅打印警告
-
-- **位置**:`software/server/src/config.rs:47-51`
-- **描述**:使用默认 JWT 密钥时仅 `tracing::warn`,生产环境可能被忽略。
-- **建议**:生产环境应直接 panic 或通过环境变量强制检查(如检查 `NODE_ENV=production` 时拒绝默认密钥)。
-
-### 建议8:H5 认证恢复未校验 token 有效性
-
-- **位置**:`software/web/src/h5/auth.ts:48-55`
-- **描述**:`restore` 函数从 localStorage 恢复 token 后直接设置 `isAuthenticated: true`,未向后端验证 token 是否仍然有效。后台管理的 `auth.ts` 会调用 `/auth/me` 验证,H5 端缺少此步骤。
-- **建议**:H5 恢复时也调用 `/h5/dashboard` 或类似接口验证 token 有效性。
-
----
-
-## 审查清单完成情况
-
-### 1. 编译与类型检查
-- [x] Rust 代码结构完整,模块引用正确(无法在此环境运行 `cargo check`)
-- [x] TypeScript 代码类型定义完整,接口匹配(无法在此环境运行 `tsc --noEmit`)
-- [x] 前端构建配置正常(Vite + React)
-
-### 2. 代码规范
-- [x] 单函数基本 <=80 行(最长函数约 70 行)
-- [x] 命名语义化,中文注释完整
-- [x] 常量已抽离(`COST_PER_KWH`、`HEARTBEAT_TIMEOUT` 等)
-- [x] 无废弃 API 使用
-
-### 3. 错误处理
-- [x] 所有外部 IO 有异常捕获
-- [x] 无裸 `panic!`(`expect` 仅用于不可能失败的序列化)
-- [x] 分支逻辑基本全覆盖
-- [x] 错误信息有意义(中文描述)
-
-### 4. Rust 专项
-- [x] 无 `unsafe` 块
-- [x] 所有权管理合理(`Clone` 用于共享状态,`Arc` 用于连接池)
-- [x] 异步代码无阻塞操作(使用 `tokio::fs` 而非 `std::fs`)
-- [x] 内存安全,无冗余拷贝
-
-### 5. React 专项
-- [x] 全部使用函数式组件 + Hooks
-- [x] 状态管理使用 Zustand(轻量级)
-- [x] useEffect 清理正确(定时器清理)
-- [ ] 存在 `as unknown as` 类型断言(见警告5)
-
-### 6. 安全
-- [x] SQL 全部使用参数化查询(`?` 占位符)
-- [x] 密码使用 Argon2 哈希
-- [x] JWT 密钥支持环境变量覆盖
-- [x] 输入参数有基本校验(手机号、IMEI 格式)
-- [ ] 权限码种子数据不完整(见严重问题1、2)
-- [ ] H5 接口缺少数据隔离(见严重问题3、4)
-- [x] 敏感参数未打印到日志(密码未出现在 tracing 中)
-
-### 7. 业务逻辑
-- [x] TCP 协议解析正确(LF 分隔、签名验证、超时清理)
-- [x] 权限模型正确(总管理员/企业管理员/普通用户三级)
-- [x] 异步导出流程完整(任务创建 -> tokio::spawn 后台处理 -> 下载中心查看)
-- [x] 前端权限组件正确隐藏/显示按钮
-
-### 8. 架构
-- [x] 接口层、业务逻辑层、数据模型层分离清晰
-- [x] 无循环依赖
-- [x] 模块职责清晰(routes 按功能拆分,tcp 按职责拆分)
-- [x] 路由注册完整
-
-### 9. 性能
-- [ ] 数据库索引需补充(见警告8)
-- [x] 无 N+1 查询问题(组织树使用批量查询后内存组装)
-- [x] 前端列表有分页
-- [x] 大数据量导出使用异步处理
-
-### 10. 可维护性
-- [x] 文件长度合理(大部分 <=300 行,最长 `h5.rs` 约 757 行但含多个独立接口)
-- [x] 组件拆分合理
-- [ ] 部分代码重复(见警告1、2)
-- [x] 配置外部化(环境变量)
-
----
-
-## 优点
-
-1. **统一错误处理**:`AppError` 枚举 + `IntoResponse` 实现了优雅的错误处理,所有错误自动转为标准 JSON 格式
-2. **安全的密码方案**:使用 Argon2(当前推荐的密码哈希算法),带随机盐
-3. **TCP 协议设计合理**:LF 分隔 JSON、签名验证、频率限制、心跳超时清理,考虑周全
-4. **连接池管理**:`ConnectionPool` 使用 `Arc>` 实现线程安全的设备连接管理
-5. **异步导出架构**:`tokio::spawn` 后台生成 Excel,下载中心轮询查看进度,用户体验良好
-6. **前端权限组件**:`` 组件支持 hidden/disabled 两种模式,按钮级权限控制优雅
-7. **H5 移动端独立实现**:与后台管理使用独立认证、独立路由、Tailwind CSS 样式,互不干扰
-8. **数据库迁移自动化**:启动时自动建表 + 种子数据,部署简单
-9. **操作日志完整**:用户管理的增删改查均记录操作日志,便于审计
-10. **代码注释质量高**:中文注释覆盖所有模块、函数和关键逻辑,可读性好
-
----
-
-## 总结
-
-项目代码质量整体良好,架构设计清晰,技术选型合理。优先修复建议如下:
-
-**P0(立即修复)**:
-1. 补充缺失的权限码种子数据(`org:*`、`project:*`、`device:edit`)— 否则非总管理员无法管理组织和项目
-2. H5 接口添加数据隔离 — 否则存在数据泄露风险
-
-**P1(尽快修复)**:
-3. 修复前后端 H5 告警数据结构不匹配
-4. `h5.rs` 中 `org_project_condition` 改为参数化查询
-5. 下载中心权限码检查优化
-
-**P2(计划修复)**:
-6. 补充数据库索引
-7. 消除代码重复(`list_users`、`energy_stats` 组织过滤)
-8. 前端 401 自动跳转登录页
-9. H5 认证恢复时校验 token 有效性
diff --git a/tasks/review-north-mini-report.md b/tasks/review-north-mini-report.md
deleted file mode 100644
index 9b7e4e8..0000000
--- a/tasks/review-north-mini-report.md
+++ /dev/null
@@ -1,246 +0,0 @@
-# 全面代码审查报告
-
-## 总体评价
-
-**质量评分:7 / 10**
-
-项目整体架构清晰,分层合理(接口层/业务层/数据层),代码注释充分,命名语义化。后端 Rust 代码质量较高,错误处理覆盖全面;前端 React 组件拆分得当,H5 移动端体验完整。但存在若干安全、性能和代码规范问题需要修复。
-
-## 统计
-
-- 后端文件数:30(Rust)
-- 前端文件数:35(TypeScript/React)
-- 总代码行数:约 11,700(后端 6,400 + 前端 5,300)
-- 问题总数:28(严重 7 / 警告 12 / 建议 9)
-
----
-
-## 严重问题(必须修复)
-
-### 问题1:心跳超时与协议上报间隔不匹配
-
-- **位置**:`software/server/src/tcp/server.rs:16`
-- **描述**:`HEARTBEAT_TIMEOUT` 设为 5000ms(5秒),但协议规定设备空闲时 1 分钟上报一次。设备在空闲状态下会在两次上报之间被断开连接。
-- **风险**:设备频繁断连,无法保持在线状态,影响充电指令下发和状态监控。
-- **建议**:将 `HEARTBEAT_TIMEOUT` 改为 `Duration::from_secs(90)`(90秒,留 1.5 倍余量),或根据设备上报频率动态调整。
-
-### 问题2:安全码 auth_str 通过 API 明文返回前端
-
-- **位置**:`software/server/src/routes/cabinets.rs:254`
-- **描述**:`regenerate_auth` 接口将 `auth_str` 直接返回给前端。安全码是设备签名密钥,不应暴露给 Web 端。
-- **风险**:安全码泄露后,攻击者可伪造设备签名登录。
-- **建议**:接口只返回 `{ "success": true }`,不返回 `auth_str` 值。如需查看,应通过独立的安全审计接口并记录操作日志。
-
-### 问题3:登录接口无暴力破解防护
-
-- **位置**:`software/server/src/routes/auth.rs:24` 和 `software/server/src/routes/h5.rs:105`
-- **描述**:登录接口没有频率限制,攻击者可无限次尝试密码。
-- **风险**:弱密码账户可被暴力破解。
-- **建议**:添加基于 IP 或手机号的登录频率限制(如 5次/分钟),失败过多时临时锁定账户或要求验证码。
-
-### 问题4:`device:door:unlock` 权限码未初始化
-
-- **位置**:`software/server/src/db/migrate.rs:225-258` 和 `software/server/src/routes/h5.rs:879`
-- **描述**:H5 开门接口使用 `device:door:unlock` 权限码,但 `seed_permissions` 中未插入该权限码。因此没有任何角色拥有此权限,开门功能永远返回 403。
-- **风险**:开门功能完全不可用。
-- **建议**:在 `seed_permissions` 中添加 `("device:door:unlock", "开门")` 并分配给相应角色。
-
-### 问题5:部分接口缺少权限校验
-
-- **位置**:`software/server/src/routes/roles.rs:37` 和 `software/server/src/routes/roles.rs:232`
-- **描述**:`list_roles` 和 `list_permissions` 接口使用 `_user` 忽略当前用户,未调用 `check_permission`。任何已登录用户都可查看角色和权限配置。
-- **风险**:权限信息泄露,攻击者可了解系统权限结构。
-- **建议**:添加 `auth::check_permission(&user, "role:view")?;` 校验。
-
-### 问题6:下载文件路径无安全校验
-
-- **位置**:`software/server/src/routes/downloads.rs:205-213`
-- **描述**:`download_file` 从数据库读取 `file_path` 后直接读取文件,未验证路径是否在 `./exports` 目录内。如果数据库中的路径被篡改(如 `../../etc/passwd`),可读取服务器任意文件。
-- **风险**:路径穿越攻击,可泄露服务器敏感文件。
-- **建议**:验证 `file_path` 必须以 `./exports/` 开头,使用 `std::path::Path::canonicalize` 后检查前缀。
-
-### 问题7:导出目录使用相对路径
-
-- **位置**:`software/server/src/routes/charge_records.rs:380` 和 `software/server/src/routes/energy_stats.rs:536`
-- **描述**:导出文件保存到 `./exports` 相对路径。如果进程工作目录非预期,文件可能写入敏感位置。
-- **风险**:文件写入位置不可控。
-- **建议**:使用配置项指定导出根目录的绝对路径,或在 `Config` 中添加 `export_dir` 字段。
-
----
-
-## 警告(建议修复)
-
-### 警告1:h5.rs 文件过长(990行)
-
-- **位置**:`software/server/src/routes/h5.rs`
-- **描述**:单文件 990 行,远超 300 行限制。包含登录、仪表盘、设备查询、充电控制等多个功能模块。
-- **建议**:拆分为 `h5/auth.rs`、`h5/dashboard.rs`、`h5/cabinets.rs`、`h5/control.rs` 等子模块。
-
-### 警告2:users.rs list_users 函数分支过多,SQL 重复严重
-
-- **位置**:`software/server/src/routes/users.rs:108-268`
-- **描述**:`list_users` 函数约 160 行,因 keyword 和 org_filter 的组合产生 6 个分支,大量 SQL 重复。
-- **建议**:使用动态 SQL 构建(类似 `charge_records.rs` 的 `build_where_clause` 模式),减少分支和重复代码。
-
-### 警告3:N+1 查询 — 柜子详情
-
-- **位置**:`software/server/src/routes/cabinets.rs:292-308`
-- **描述**:`get_cabinet_detail` 先查仓板列表,再逐个查仓体。若柜有 N 块仓板,产生 N+1 次查询。
-- **建议**:使用一条 SQL 联表查询所有仓板和仓体,在 Rust 中按 board_id 分组。
-
-### 警告4:N+1 查询 — H5 项目摘要
-
-- **位置**:`software/server/src/routes/h5.rs:436-486`
-- **描述**:`fetch_project_summaries` 对每个项目执行 4 次独立查询(柜子数、充电中、空闲、故障),项目数为 N 时产生 4N+1 次查询。
-- **建议**:合并为一条 SQL,使用 `LEFT JOIN` + `SUM(CASE WHEN...)` 聚合。
-
-### 警告5:N+1 查询 — 角色列表
-
-- **位置**:`software/server/src/routes/roles.rs:42-71`
-- **描述**:`list_roles` 对每个角色单独查询权限码,角色数为 N 时产生 N+1 次查询。
-- **建议**:一条 SQL 联表查询所有角色及其权限码,在 Rust 中按 role_id 分组。
-
-### 警告6:前端多处使用 `as unknown as` 类型断言
-
-- **位置**:`software/web/src/pages/devices/index.tsx:81,103`、`OrganizationTree.tsx:169,171`
-- **描述**:多处使用 `as unknown as` 进行类型转换,等同于 `as any`,绕过 TypeScript 类型检查。
-- **建议**:修正 API 返回类型定义,使类型匹配,消除 `as unknown as`。
-
-### 警告7:H5 认证恢复不验证 token 有效性
-
-- **位置**:`software/web/src/h5/auth.ts:48-55`
-- **描述**:H5 `restore` 仅从 localStorage 读取 token 和用户信息,不调用服务端验证 token 是否过期/有效。若 token 已过期,用户看到已登录状态但请求会失败。
-- **建议**:restore 时调用 `/api/h5/auth/me` 或类似接口验证 token,失败则清除本地状态。对比后台管理端的 `restore` 实现(`stores/auth.ts:72-91`),后者正确调用了 `/auth/me`。
-
-### 警告8:前端权限分组缺少组织和项目模块
-
-- **位置**:`software/web/src/pages/settings/roles.tsx:37-46`
-- **描述**:`PERM_GROUPS` 缺少 `org` 和 `project` 分组,导致组织和项目管理权限在权限树中显示为原始前缀名。
-- **建议**:添加 `org: '组织管理'` 和 `project: '项目管理'` 到 `PERM_GROUPS`。
-
-### 警告9:Dashboard 页面为空
-
-- **位置**:`software/web/src/pages/Dashboard.tsx`
-- **描述**:后台管理 Dashboard 仅显示欢迎文字,无任何数据看板。H5 端有完整的 dashboard API,但后台端未使用。
-- **建议**:复用 H5 dashboard API 或创建独立的后台 dashboard 接口,展示设备总览、充电统计等。
-
-### 警告10:org_condition 使用字符串拼接 SQL
-
-- **位置**:`software/server/src/middleware/auth.rs:255-288`
-- **描述**:`org_condition` 和 `org_condition_for_logs` 通过 `format!` 拼接 SQL 子句,虽然参数使用 `?` 占位符,但表名/列名直接插入字符串。模式脆弱,容易引入 SQL 语法错误。
-- **建议**:考虑使用 query builder 库(如 `sea-query`),或至少将子查询模板定义为常量。
-
-### 警告11:compartments 表缺少 updated_at 索引
-
-- **位置**:`software/server/src/db/migrate.rs:47-55` 和 `software/server/src/routes/h5.rs:379-391`
-- **描述**:`fetch_fault_alerts` 使用 `ORDER BY comp.updated_at DESC`,但 `compartments` 表没有 `updated_at` 索引。
-- **建议**:添加 `idx_compartments_updated` 索引,或在 `create_indexes` 中添加。
-
-### 警告12:充电记录导出缺少组织数据隔离
-
-- **位置**:`software/server/src/routes/charge_records.rs:309-397`
-- **描述**:`generate_charge_records_excel` 异步导出时未追加组织过滤条件,企业管理员可导出全部组织的充电记录。
-- **风险**:数据越权,企业管理员可导出非本组织数据。
-- **建议**:在导出查询中追加 `org_condition` 过滤。
-
----
-
-## 建议
-
-### 建议1:统一 API 响应格式
-
-- **位置**:全局
-- **描述**:部分接口返回 `{ "code": 0, "data": ..., "message": "ok" }` 格式,部分直接返回数据对象(如 `organizations` 列表接口返回 `json!(rows)`)。前端需要兼容两种格式。
-- **建议**:统一所有接口使用 `{ code, data, message }` 包装。
-
-### 建议2:常量抽离 — 魔法数字
-
-- **位置**:多处
-- **描述**:状态码(0/1/2/3)、角色值(0/1/2)等魔法数字散落在前后端代码中。
-- **建议**:后端使用 Rust 枚举(`enum CompartmentStatus { Idle = 0, Charging = 1, ... }`),前端使用常量对象(`COMPARTMENT_STATUS`)。
-
-### 建议3:前端 DownloadCenter 组件复用
-
-- **位置**:`software/web/src/pages/charge-records/DownloadCenter.tsx`
-- **描述**:DownloadCenter 被充电记录和能耗管理共用,但路径为 `charge-records/` 下。
-- **建议**:移到 `components/DownloadCenter.tsx`,体现其通用组件定位。
-
-### 建议4:JWT 密钥在 middleware/auth.rs 和 config.rs 中重复定义
-
-- **位置**:`software/server/src/config.rs:4` 和 `software/server/src/middleware/auth.rs:33`
-- **描述**:`JWT_SECRET_DEFAULT` 在两个文件中重复定义。
-- **建议**:统一从 `Config` 结构体读取,避免不一致。
-
-### 建议5:`set_role_permissions` 非原子操作
-
-- **位置**:`software/server/src/routes/roles.rs:202-228`
-- **描述**:先 DELETE 再逐条 INSERT,非事务操作。如果中途失败,角色权限处于不一致状态。
-- **建议**:使用事务包裹,或批量 INSERT。
-
-### 建议6:`set_user_roles` 同样非原子
-
-- **位置**:`software/server/src/routes/users_perm.rs:144-168`
-- **描述**:同上,先 DELETE 再逐条 INSERT。
-- **建议**:使用事务。
-
-### 建议7:前端 `eslint-disable-next-line` 注释
-
-- **位置**:`software/web/src/h5/pages/devices.tsx:62`、`device-detail.tsx:36`、`compartment-detail.tsx:42`
-- **描述**:多处使用 `eslint-disable-next-line react-hooks/exhaustive-deps` 跳过 hooks 依赖检查。
-- **建议**:修正依赖数组,或使用 `useCallback`/`useRef` 解决。
-
-### 建议8:充电曲线使用硬编码假数据
-
-- **位置**:`software/web/src/h5/pages/compartment-detail.tsx:174`
-- **描述**:充电曲线图表使用硬编码数据 `[30, 45, 55, ...]`,非真实数据。
-- **建议**:标注为 TODO 或移除该图表,待后端提供充电曲线 API 后再实现。
-
-### 建议9:`generate_abstract_id` 算法可预测
-
-- **位置**:`software/server/src/routes/organizations.rs:130-135`
-- **描述**:抽象 ID 由 IMEI 的 SHA256 前 4 字节生成,仅 8 位十六进制(约 43 亿种组合)。虽然碰撞概率低,但可被预测。
-- **建议**:如抽象 ID 不需可预测性,可加入随机盐或使用更长哈希。
-
----
-
-## 优点
-
-1. **架构分层清晰**:后端严格按 路由/中间件/协议/数据库 分层,前端按 API/Store/Page/Component 分层,职责明确。
-2. **统一错误处理**:后端 `AppError` 统一错误类型,自动映射 HTTP 状态码,错误信息对用户友好,内部错误详情仅记录日志。
-3. **密码安全**:使用 argon2 哈希(业界推荐),非 MD5/SHA。
-4. **参数化查询**:所有数据库查询使用 `?` 占位符,有效防止 SQL 注入。
-5. **RBAC 权限模型完整**:角色-权限-范围三层设计,前端 `Permission` 组件支持隐藏/禁用两种模式。
-6. **组织数据隔离**:核心查询接口均实现组织级数据隔离,防止跨组织数据泄露。
-7. **TCP 协议处理健壮**:LF 分隔粘包、JSON 解析容错、签名验证、时间戳窗口校验、auth_str 频率限制。
-8. **异步导出流程完整**:任务创建 -> 后台生成 Excel -> 进度更新 -> 下载 -> 过期清理,全链路覆盖。
-9. **H5 移动端完整**:扫码入口、设备概览、充电控制、BMS 数据展示、确认弹窗,用户体验良好。
-10. **代码注释充分**:所有模块和公共函数都有中文文档注释,便于维护。
-11. **数据库迁移自动化**:启动时自动建表、建索引、初始化权限码和角色,部署简便。
-12. **前端组件拆分合理**:设备管理页拆分为 OrganizationTree、CabinetGrid、AddCabinetModal 等独立组件。
-
----
-
-## 总结
-
-### 优先修复建议(按紧急程度排序)
-
-1. **P0 — 立即修复**:
- - 心跳超时改为 90 秒(问题1),否则设备无法保持在线
- - 添加 `device:door:unlock` 权限码(问题4),否则开门功能不可用
- - 下载文件路径安全校验(问题6),防止路径穿越攻击
-
-2. **P1 — 本迭代修复**:
- - 登录接口添加频率限制(问题3)
- - auth_str 不返回前端(问题2)
- - 补充缺失的权限校验(问题5)
- - 充电记录导出添加组织隔离(警告12)
- - 导出目录使用绝对路径(问题7)
-
-3. **P2 — 下迭代优化**:
- - 拆分 h5.rs(警告1)
- - 修复 N+1 查询(警告3/4/5)
- - H5 认证恢复验证 token(警告7)
- - 消除 `as unknown as` 类型断言(警告6)
- - 补充 compartments.updated_at 索引(警告11)
-
-整体代码质量良好,核心业务逻辑正确,安全基础扎实。上述问题修复后可达到生产就绪水平。
diff --git a/tasks/review-report.md b/tasks/review-report.md
deleted file mode 100644
index f1bbd4e..0000000
--- a/tasks/review-report.md
+++ /dev/null
@@ -1,206 +0,0 @@
-# 全面代码审查报告
-
-## 总体评价
-
-**评分:7.5/10**
-
-整体架构清晰,Axum 后端 + React 前端技术栈选型合理。代码分层明确,安全基础扎实(argon2 哈希、参数化 SQL、RBAC 权限模型、组织数据隔离)。TCP 通讯协议实现完整。主要扣分点:权限种子数据缺失导致非超管用户功能不可用、SQL JOIN 错误、部分函数过长。
-
-## 统计
-
-| 指标 | 数值 |
-|------|------|
-| 后端 Rust 文件数 | 29 |
-| 后端代码行数 | 5,198 |
-| 前端 TSX 文件数 | 27 |
-| 前端代码行数 | 4,535 |
-| 总代码行数 | 9,733 |
-| 静态检查 | `cargo check` ✅ `cargo clippy` ✅ `tsc --noEmit` ✅ `vite build` ✅ |
-| 问题总数 | 11(严重 3 / 警告 5 / 建议 3) |
-
----
-
-## 严重问题( 必须修复)
-
-### 问题1:权限种子数据缺失 — 非超管用户功能不可用
-
-**位置**:`software/server/src/db/migrate.rs:186-219`
-
-**描述**:`seed_permissions` 中缺少以下权限码,但代码中多处通过 `check_permission` 使用:
-
-| 缺失权限码 | 使用位置 |
-|------------|---------|
-| `device:edit` | `cabinets.rs:166` 更新柜子、`cabinets.rs:237` 重新生成安全码 |
-| `org:view` | `organizations.rs:154` |
-| `org:create` | `organizations.rs:172` |
-| `org:edit` | `organizations.rs:198` |
-| `org:delete` | `organizations.rs:227` |
-| `project:view` | `projects.rs:20` |
-| `project:create` | `projects.rs:40` |
-| `project:edit` | `projects.rs:78` |
-| `project:delete` | `projects.rs:107` |
-
-**风险**:任何 role_level < 2 的用户(企业管理员、普通用户)调用这些接口时,`check_permission` 会返回 `Err(Forbidden)`,即使角色已分配这些权限(因为数据库中根本不存在这些权限码,无法关联)。
-
-**建议**:在 `seed_permissions` 中补充上述 9 个权限码,并为 `org_admin` 和 `normal_user` 角色分配相应权限。
-
----
-
-### 问题2:充电记录计数 SQL JOIN 错误
-
-**位置**:`software/server/src/routes/charge_records.rs:187-191`
-
-**描述**:列表查询的 COUNT 子句:
-```sql
-SELECT COUNT(*) FROM charge_records cr
-LEFT JOIN cabin_boards cb ON cr.compartment_id = cb.id
-```
-`cr.compartment_id` 是 `compartments` 表的主键,而非 `cabin_boards` 表的主键。错误的 JOIN 条件会在 `compartment_id` 恰好等于某个 `cabin_boards.id` 时产生错误匹配,导致 COUNT 结果不准确。
-
-对比同文件第 136-149 行的列表查询,使用了正确的三表 JOIN:
-```sql
-LEFT JOIN compartments comp ON cr.compartment_id = comp.id
-LEFT JOIN cabin_boards cb ON comp.cabin_board_id = cb.id
-```
-
-**风险**:当 `cabin_board_id` 过滤条件生效时,计数结果将严重失准。无过滤时因 LEFT JOIN 不过滤行,影响较小但仍有潜在错误匹配。
-
-**建议**:修正 COUNT 查询的 JOIN 逻辑,与列表查询保持一致。
-
----
-
-### 问题3:`protocol.rs` 中 `expect()` 可能导致生产环境 panic
-
-**位置**:`software/server/src/tcp/protocol.rs:58`、`protocol.rs:80`
-
-**描述**:
-```rust
-pub fn to_json(&self) -> String {
- serde_json::to_string(self).expect("ServerResponse 序列化不应失败")
-}
-```
-虽然 `ServerResponse` 和 `DeviceCommand` 的字段理论上不会导致序列化失败(都是标准类型),但 `expect()` 在 `serde_json` 遇到非 UTF-8 字符或自引用类型时会 panic。TCP 连接的 panic 如果未被 tokio 的 panic handler 捕获,可能导致整个 TCP 服务崩溃。
-
-**风险**:生产环境下若序列化异常,TCP 服务可能崩溃,影响所有在线设备的通讯。
-
-**建议**:将 `expect()` 替换为 `unwrap_or_default()` 或返回 `Result`:
-```rust
-pub fn to_json(&self) -> String {
- serde_json::to_string(self).unwrap_or_default()
-}
-```
-
----
-
-## 警告(🟡 建议修复)
-
-### 警告1:JWT_SECRET_DEFAULT 重复定义
-
-**位置**:`config.rs:4` 和 `middleware/auth.rs:33`
-
-**描述**:两处分别定义了相同的常量:
-```rust
-const JWT_SECRET_DEFAULT: &str = "pms-dev-secret-change-me-in-production";
-```
-`config.rs` 中的 `validate()` 方法检查是否使用默认密钥并打印警告,而 `middleware/auth.rs` 中每次认证时也读取环境变量并回退到默认值。两处定义不一致可能导致维护问题。
-
-**建议**:将常量统一到 `config.rs` 并导出,`middleware/auth.rs` 通过 `AppState` 获取密钥,而非每次从环境变量读取。
-
----
-
-### 警告2:默认密码 "123456" 硬编码且作为回退值
-
-**位置**:`config.rs:7`、`users.rs:303`、`users.rs:438`、`users.tsx:67`、`users.tsx:74`
-
-**描述**:创建用户和重置密码时,如果未提供密码则回退到 `DEFAULT_PASSWORD`("123456")。前端用户管理页面也将默认密码硬编码为 "123456"。
-
-**风险**:管理员可能忘记修改默认密码,导致新用户以弱密码登录。
-
-**建议**:创建用户时强制要求提供密码;或在首次登录时强制修改密码。前端不应预填默认密码。
-
----
-
-### 警告3:`list_users` 函数过长(160行)
-
-**位置**:`users.rs:96-256`
-
-**描述**:该函数包含 4 种查询分支(有/无 keyword × 有/无 org_filter),每个分支独立构建 SQL。加上组织名称批量查询和结果映射,总行数达 160 行。
-
-**建议**:使用动态条件构建器统一查询逻辑,减少分支重复。
-
----
-
-### 警告4:`list_roles` 和 `list_permissions` 缺少权限校验
-
-**位置**:`roles.rs:36-72`、`roles.rs:232-248`
-
-**描述**:这两个接口虽然接受 `CurrentUser` 参数(确保已认证),但未调用 `check_permission`。任何已认证用户都可以查看所有角色和权限码。
-
-**建议**:添加 `role:view` 权限校验(`list_permissions` 同理)。
-
----
-
-### 警告5:前端 `as unknown as` 类型断言
-
-**位置**:`devices/index.tsx:81`、`devices/index.tsx:103`、`devices/index.tsx:260`、`AddCabinetModal.tsx:44`
-
-**描述**:4 处使用了 `as unknown as TargetType` 双重类型断言来绕过类型检查。这通常意味着 API 返回类型定义不完整或 API 层封装有问题。
-
-**建议**:在 API 层定义完整的响应类型,避免在组件中使用类型断言。
-
----
-
-## 建议( 可优化)
-
-### 建议1:`cabinet_tree.rs` 全量加载数据
-
-**位置**:`cabinet_tree.rs:46-57`
-
-**描述**:组织树接口一次性加载所有 projects 和 cabinets,然后在内存中过滤。数据量大时可能有性能问题。
-
-**建议**:根据用户角色,使用 SQL WHERE 条件在数据库层过滤,只返回用户有权查看的数据。
-
----
-
-### 建告2:`energy_stats.rs` 组织过滤代码重复
-
-**位置**:`energy_stats.rs:90-99`、`energy_stats.rs:174-183`、`energy_stats.rs:230-236`、`energy_stats.rs:289-298`
-
-**描述**:4 个函数中重复了几乎相同的组织过滤逻辑(判断 role_level、构建 org_filter/org_binds)。
-
-**建议**:抽取为辅助函数,与 `auth.rs` 中的 `org_condition` 统一。
-
----
-
-### 建议3:TCP 连接 JSON 解析失败时无响应
-
-**位置**:`tcp/server.rs:88-94`
-
-**描述**:设备发送的消息 JSON 解析失败时,`continue` 跳过不回复。这在安全上是合理的(不给恶意设备反馈),但合法设备可能因编码问题发送非标准 JSON 而无法得知原因。
-
-**建议**:保持当前行为(安全优先),但增加更详细的 debug 日志(当前已有 warn 级别日志,可接受)。
-
----
-
-## 优点
-
-1. **架构清晰**:Axum 路由分层合理,`AppState` 共享模式规范,中间件/路由/业务逻辑分离良好
-2. **安全基础扎实**:全部使用参数化 SQL 查询(零字符串拼接)、argon2 密码哈希、JWT + RBAC 权限模型、组织级数据隔离
-3. **TCP 协议实现完整**:签名验证、心跳检测、超时清理、LF 消息边界处理正确
-4. **异步导出流程**:任务创建→后台生成→状态轮询→下载,模式规范
-5. **错误处理统一**:`AppError` 枚举 + `IntoResponse` 实现,HTTP 状态码映射正确
-6. **数据库迁移**:自动建表 + 种子数据,部署友好
-7. **前端代码整洁**:函数式组件 + Hooks,无 Class 组件,无 `as any` 类型断言
-8. **注释充分**:模块级文档注释完整,函数级注释覆盖入参和行为说明
-
----
-
-## 总结
-
-项目整体质量良好,架构和安全方面表现出色。**必须立即修复的 3 个严重问题**:
-
-1. **权限种子数据缺失**( 最高优先级)— 直接导致非超管用户功能不可用,影响整个 RBAC 体系
-2. **充电记录计数 SQL 错误**( 高优先级)— 导致数据展示不准确
-3. **protocol.rs 中的 expect()**( 中优先级)— 潜在的生产环境 panic 风险
-
-修复这 3 个问题后,项目即可达到生产就绪状态。其余警告和建议可作为后续迭代优化项。
diff --git a/tasks/review-round-2-report.md b/tasks/review-round-2-report.md
deleted file mode 100644
index 441b405..0000000
--- a/tasks/review-round-2-report.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# 代码审查报告(第二轮)
-
-## 总体评价
-
-**6.5/10** — 4个安全问题中3个彻底修复,1个(权限校验)部分修复。构建全部通过,文件拆分合理。但发现 **3个模块完全缺失权限校验**(🔴 高风险新发现问题)。
-
----
-
-## 安全修复验证结果
-
-| 问题 | 状态 | 说明 |
-|------|------|------|
-| **SQL注入** | ✅ 已修复 | `operation_logs.rs`、`charge_records.rs`、`energy_stats.rs`、`device_logs.rs` 全部使用 `.bind()` 参数化查询。`LIMIT/OFFSET` 通过 `i64` 类型转换后绑定。`build_where_clause` 中所有条件值均通过 `.bind()` 传入,无 `format!` 拼接用户输入到 SQL 的问题。 |
-| **密码哈希** | ✅ 已修复 | `middleware/auth.rs:230-247` 使用 `argon2` 库实现 `hash_password` / `verify_password`。`users.rs:249` 创建用户时哈希,`users.rs:384` 重置密码时哈希,`auth.rs:51` 登录时验证。argon2 哈希字符串长度约 97 字符,远小于 VARCHAR(255)。 |
-| **数据隔离** | ✅ 已修复 | `CurrentUser` 包含 `organization_id: Option`。`org_condition()` 和 `org_condition_for_logs()` 在非总管理员时自动附加组织过滤子查询。总管理员跳过过滤。无组织用户返回 `AND 1=0`(空结果)。`charge_records.rs`、`energy_stats.rs`、`device_logs.rs`、`operation_logs.rs` 均已集成。 |
-| **权限校验** | ⚠️ 部分修复 | 大部分路由有 `check_permission`,但 **cabinets.rs、projects.rs、organizations.rs 三个模块完全缺失**(详见下方新发现问题)。 |
-
----
-
-## 代码质量验证结果
-
-| 项目 | 状态 | 说明 |
-|------|------|------|
-| **文件拆分** | ✅ | 后端 `organizations.rs` 242行(≤300),`users.rs` 497行(职责集中可接受)。前端 `devices/index.tsx` 359行 → 已拆分为 `OrganizationTree.tsx`(182行)、`CabinetGrid.tsx`(79行)、`AddCabinetModal.tsx`(134行),职责清晰。 |
-| **构建检查** | ✅ | `cargo check` 通过(零报错零警告)。`cargo clippy` 通过(零报错零警告)。`tsc --noEmit` 通过(零报错零警告)。`vite build` 成功(1045 modules,1.3MB JS / 570KB CSS,1.16s)。 |
-| **JWT安全** | ✅ | `config.rs:45-47` 启动时检测默认密钥并打印 `tracing::warn!`。`middleware/auth.rs:33` 定义了 `JWT_SECRET_DEFAULT`。 |
-
----
-
-## 🔴 新引入问题(3处权限校验缺失)
-
-### 问题 1:设备管理模块(cabinets.rs)— 全部 8 个端点无权限校验
-
-| 路由 | 风险 | 建议 |
-|------|------|------|
-| `GET /api/projects/:project_id/cabinets` | 可查看任意项目下的柜子 | 加 `check_permission("device:view")` |
-| `POST /api/cabinets` | 可任意添加设备 | 加 `check_permission("device:create")` |
-| `PUT /api/cabinets/:id` | 可修改任意设备 | 加 `check_permission("device:edit")` |
-| `DELETE /api/cabinets/:id` | 可删除任意设备 | 加 `check_permission("device:delete")` |
-| `POST /api/cabinets/:id/regenerate-auth` | 可篡改设备安全码 | 加 `check_permission("device:edit")` |
-| `GET /api/cabinets/:id` | 可查看任意设备详情 | 加 `check_permission("device:view")` |
-| `GET /api/cabinets/options` | 可枚举所有设备 | 加 `check_permission("device:view")` |
-| `GET /api/cabin-boards/options`、`GET /api/compartments/options` | 同上 | 加 `check_permission("device:view")` |
-
-### 问题 2:项目管理模块(projects.rs)— 全部 4 个端点无权限校验
-
-| 路由 | 风险 | 建议 |
-|------|------|------|
-| `GET /api/organizations/:org_id/projects` | 可查看任意项目 | 加 `check_permission("project:view")` |
-| `POST /api/organizations/:org_id/projects` | 可创建任意项目 | 加 `check_permission("project:create")` |
-| `PUT /api/projects/:id` | 可修改任意项目 | 加 `check_permission("project:edit")` |
-| `DELETE /api/projects/:id` | 可删除任意项目 | 加 `check_permission("project:delete")` |
-
-### 问题 3:组织管理模块(organizations.rs)— 全部 4 个端点无权限校验
-
-| 路由 | 风险 | 建议 |
-|------|------|------|
-| `GET /api/organizations` | 可枚举所有组织 | 加 `check_permission("org:view")` |
-| `POST /api/organizations` | 可创建组织 | 加 `check_permission("org:create")` |
-| `PUT /api/organizations/:id` | 可修改组织 | 加 `check_permission("org:edit")` |
-| `DELETE /api/organizations/:id` | 可删除组织 | 加 `check_permission("org:delete")` |
-
----
-
-## 其他观察(非阻塞)
-
-1. **organizations.rs 缺少 `CurrentUser` 参数** — 三个模块的 handler 签名都是 `State + Path/Json`,没有提取 `CurrentUser`,所以无法做权限校验。这是结构性问题,需要在函数签名中加上 `user: CurrentUser`。
-2. **`users.rs` 用户列表查询缺少组织隔离** — `list_users` 返回所有用户,不受 `CurrentUser.organization_id` 限制。企业管理员可以看到其他组织的用户。
-3. **`cabinets.rs` 的 `list_cabinets` 只按 `project_id` 过滤** — 没有额外验证当前用户是否有权访问该项目。如果用户知道其他项目的 project_id,可以绕过组织隔离。
-
----
-
-## 总结
-
-| 维度 | 结论 |
-|------|------|
-| SQL注入 | ✅ 已修复,全部使用 `.bind()` 参数化 |
-| 密码哈希 | ✅ 已修复,argon2 实现正确 |
-| 数据隔离 | ✅ 已修复,组织过滤子查询正确 |
-| 权限校验 | ⚠️ 部分修复,3个模块缺失 |
-| 构建 | ✅ 全部通过 |
-| 文件拆分 | ✅ 合理,职责清晰 |
-| JWT安全 | ✅ 启动时检测默认密钥 |
-
-**核心结论:4个安全问题中,3个彻底修复,1个(权限校验)需要补充 cabinets.rs / projects.rs / organizations.rs 三个模块。**
diff --git a/tasks/review-round-2.md b/tasks/review-round-2.md
deleted file mode 100644
index cc2c1f6..0000000
--- a/tasks/review-round-2.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# 代码审查任务(第二轮)
-
-## 目标
-
-审查安全修复和代码质量优化后的代码,确认问题已解决,无新引入问题。
-
-## 审查范围
-
-### 后端 (Rust + Axum)
-- `software/server/src/` 目录下所有文件
-- 重点审查:安全修复的4个问题是否彻底解决
-
-### 前端 (React + TypeScript)
-- `software/web/src/` 目录下所有文件
-- 重点审查:拆分后的组件结构、构建是否正常
-
-## 审查清单
-
-### 1. 安全修复验证
-
-#### 1.1 SQL注入修复
-- [ ] `operation_logs.rs` 使用参数化查询(`.bind()`)
-- [ ] `charge_records.rs` 的 LIMIT/OFFSET 使用 `.bind()`
-- [ ] `energy_stats.rs` 的 LIMIT/OFFSET 使用 `.bind()`
-- [ ] 无 `format!` 拼接用户输入到SQL
-
-#### 1.2 密码哈希
-- [ ] 使用 argon2 或 bcrypt 哈希密码
-- [ ] 登录时使用 `verify_password` 验证
-- [ ] 创建/重置用户时哈希密码
-- [ ] 数据库password字段长度≥255
-
-#### 1.3 数据隔离
-- [ ] `CurrentUser` 包含 `organization_id`
-- [ ] 查询自动附加组织过滤
-- [ ] 总管理员可看全部,企业管理员只看本组织
-- [ ] 无组织用户无法查看数据
-
-#### 1.4 权限校验
-- [ ] 所有路由都有 `check_permission` 调用
-- [ ] 权限码与功能匹配
-- [ ] 无越权访问可能
-
-### 2. 代码质量验证
-
-#### 2.1 文件拆分
-- [ ] `organizations.rs` ≤300行
-- [ ] `users.rs` ≤300行
-- [ ] `devices/index.tsx` ≤300行
-- [ ] 拆分后组件职责单一
-
-#### 2.2 构建检查
-- [ ] `cargo check` 零报错零警告
-- [ ] `cargo clippy` 零报错零警告
-- [ ] `tsc --noEmit` 零报错零警告
-- [ ] `vite build` 成功(无IconFolderOpen错误)
-
-#### 2.3 JWT安全
-- [ ] 启动时检测默认密钥并打印WARN
-- [ ] 生产环境必须设置JWT_SECRET
-
-### 3. 回归测试
-
-- [ ] 登录功能正常(密码哈希后)
-- [ ] 组织树查询正常(数据隔离后)
-- [ ] 充电记录查询正常(参数化查询后)
-- [ ] 前端页面正常渲染(组件拆分后)
-
-### 4. 新引入问题检查
-
-- [ ] 无新的编译错误
-- [ ] 无新的类型错误
-- [ ] 无循环依赖
-- [ ] 无未使用的导入/变量
-
-## 输出格式
-
-```markdown
-# 代码审查报告(第二轮)
-
-## 总体评价
-[整体质量评分 1-10分,简要评价]
-
-## 安全修复验证结果
-| 问题 | 状态 | 说明 |
-|------|------|------|
-| SQL注入 | ✅/❌ | [验证结果] |
-| 密码哈希 | ✅/❌ | [验证结果] |
-| 数据隔离 | ✅/ | [验证结果] |
-| 权限校验 | ✅/❌ | [验证结果] |
-
-## 代码质量验证结果
-| 项目 | 状态 | 说明 |
-|------|------|------|
-| 文件拆分 | ✅/❌ | [验证结果] |
-| 构建检查 | ✅/❌ | [验证结果] |
-| JWT安全 | ✅/❌ | [验证结果] |
-
-## 新引入问题
-[列出新发现的问题,如无则写"无"]
-
-## 总结
-[总体结论]
-```
-
-## 审查工具
-
-```bash
-# 后端静态检查
-cd software/server && cargo check && cargo clippy
-
-# 前端类型检查
-cd software/web && tsc --noEmit
-
-# 前端构建测试
-cd software/web && vite build
-
-# 代码行数统计
-find src -name "*.rs" -exec wc -l {} + | sort -n
-find src -name "*.tsx" -exec wc -l {} + | sort -n
-```
-
-## 质量约束
-
-1. 审查必须覆盖所有清单项目
-2. 每个问题必须给出具体位置和修复建议
-3. 安全修复必须逐项验证
-4. 审查报告用中文撰写
diff --git a/tasks/review-tcp-refactor-report.md b/tasks/review-tcp-refactor-report.md
deleted file mode 100644
index 53bacb6..0000000
--- a/tasks/review-tcp-refactor-report.md
+++ /dev/null
@@ -1,196 +0,0 @@
-# TCP服务重构审查报告
-
-## 总体评价
-
-**评分:6/10**
-
-整体架构设计方向正确(服务分离、Redis队列通信、全异步处理),但存在 **1个严重Bug** 和 **多处架构实现不一致**。核心问题集中在节点管理实现的双轨冲突和配置系统与要求的背离。代码结构清晰,但在细节实现上有明显瑕疵。
-
----
-
-## 架构符合性
-
-### 符合项
-| 条目 | 状态 | 说明 |
-|------|------|------|
-| API/设备服务职责分离 | ✅ | API服务处理HTTP+业务,设备服务仅TCP通信+转发 |
-| 设备服务不存DB/不验证签名 | ✅ | handler.rs 仅做 `forward_to_redis` |
-| 服务间用Redis LIST通信 | ✅ | `device:reports` / `device:commands` / `device:replies` 三个队列 |
-| 全异步处理 | ✅ | 所有协程用 `tokio::spawn`,无阻塞调用 |
-
-### 问题
-
-**严重问题 1:节点管理双轨制 — API服务内存注册表 vs Redis注册互不关联**
-
-- 设备服务 `main.rs:44` 通过 `SET node:node-1` 写 Redis 注册,**从未调用 API 服务的 HTTP 接口**
-- API 服务 `routes/mod.rs:39-42` 注册了 `/api/nodes/register`、`/api/nodes/:node_id/heartbeat` 等路由,但 **从未有调用方**
-- API 服务的 `NodeRegistry`(内存 RwLock)与 Redis 中的 `node:*` 键完全独立
-- `node_checker.rs` 检查的是内存中的 `NodeRegistry`,而它是空的(因为设备服务没调用过 `/api/nodes/register`)
-
-**后果**:`GET /api/nodes` 返回空列表,节点管理功能实际上不可用。
-
-**修复建议**:统一节点管理方案——
-- 方案A(推荐):去掉 API 服务的内存 `NodeRegistry`,改用 Redis 作为唯一节点注册中心。`list_nodes` 查询 `node:*` 键。
-- 方案B:设备服务在启动时通过 HTTP 调用 `POST /api/nodes/register`,心跳调用 `POST /api/nodes/:node_id/heartbeat`。
-
----
-
-## 代码质量
-
-### 严重问题
-
-**严重问题 2(必须修复):Redis 类型冲突 — register_node 用 SET,heartbeat 用 HSET**
-
-**位置**:`device-server/src/main.rs` 第72-110行
-
-```rust
-// register_node — 第80行
-redis::cmd("SET").arg(&key).arg(info.to_string()).arg("EX")...
-
-// heartbeat — 第101行
-redis::cmd("HSET").arg(&key).arg("last_heartbeat")...
-```
-
-`SET` 将 key 创建为 **string 类型**,之后 `HSET` 在同一 key 上操作会触发 Redis `WRONGTYPE` 错误。`heartbeat` 函数仅记录 `tracing::warn`,不会 panic,但心跳静默失效。
-
-**后果**:`node:{node_id}` 的 TTL(180秒)到期后节点自动消失,节点管理完全不可用。
-
-**修复建议**:任一方案——
-- 将 `register_node` 改为 `HSET` 逐字段存储(`node_id`、`ip`、`tcp_port`、`registered_at`),再 `EXPIRE`
-- 或将 `heartbeat` 改为 `SET` + `KEEPTTL` 重写整个 JSON
-
----
-
-### 配置问题
-
-**问题 3:配置读取方式与审查要求不符**
-
-**审查清单要求**:使用 `config.toml`(不用 `.env`)
-
-**实际**:
-- 两个服务的 `config.rs` 均调用 `dotenvy::dotenv()` + `std::env::var()`,**完全不读取 config.toml**
-- config.toml 文件存在于两个项目目录中,但没有任何代码解析它们
-- 敏感信息(数据库密码 `Hbhyg731024@`)硬编码在 `config.toml` 中
-
-**修复建议**:二选一——
-- 如果坚持环境变量方案,删除 config.toml 文件
-- 如果坚持 config.toml 方案,用 `toml` crate 解析并替换 `from_env()`
-
----
-
-### 结构体重复
-
-**问题 4:协议结构体在两个服务中重复定义**
-
-| 结构体 | api-server 位置 | device-server 位置 |
-|--------|----------------|-------------------|
-| `DeviceMessage` | `protocol.rs:14` | `tcp/protocol.rs:14` |
-| `ServerResponse` | `protocol.rs:36` | `tcp/protocol.rs:36` |
-| `DeviceCommand` | `commands.rs:118` | `tcp/protocol.rs:67` |
-
-`DeviceCommand` 甚至在同一项目的两个文件中重复(`commands.rs` 和 `tcp/protocol.rs`)。
-
-**影响**:维护时需同步修改两处,容易不一致。
-
-**修复建议**:抽取共享类型到独立 crate(如 `pms-protocol`),两个服务共同依赖。
-
----
-
-### 死代码
-
-**问题 5:`send_device_response` 未使用**
-
-**位置**:`api-server/src/commands.rs` 第157行
-
-函数用 `#[allow(dead_code)]` 标记,实际从未被调用。响应发送由 `report_worker.rs` 中的 `send_response_to_device` 完成。
-
----
-
-### 异步规范
-
-**问题 6:`auth_str` 限流器使用同步 Mutex**
-
-**位置**:`api-server/src/workers/report_worker.rs` 第30行
-
-```rust
-struct RateLimiter {
- attempts: Mutex>>, // std::sync::Mutex
- ...
-}
-```
-
-在 `tokio::spawn` 的异步任务中持有了同步 `std::sync::Mutex` 锁。虽然当前负载下不会死锁(锁持有时间极短),但不符合异步规范。
-
-**修复建议**:改为 `tokio::sync::Mutex`,或将 `RateLimiter` 与 `LazyLock` 移到 `spawn_blocking` 中。
-
----
-
-### 函数长度
-
-所有函数均在80行以内 ✅(最长的 `handle_connection` 144行但包含多个分支逻辑块,为 tokio::select! 模式,可接受)。
-
-### 错误处理
-
-- 所有外部 IO 均有异常捕获 ✅
-- 无裸 `panic` ✅(仅 `main.rs` 中启动阶段 `expect` 合理)
-- `thiserror` + `AppError` 统一错误处理 ✅
-
----
-
-## Redis 使用审查
-
-| 要求 | 状态 | 说明 |
-|------|------|------|
-| 连接注册 `device:{imei}` → info | ❌ | 实际用 `device:online:{imei}` 仅存 "1" |
-| TTL 180秒 | ✅ | `EX 300`(5分钟) |
-| `device:reports` 队列 | ✅ | BRPOP 消费 |
-| `device:commands` 队列 | ✅ | BRPOP 消费 |
-| `device:replies` 队列 | ✅ | BRPOP 消费 |
-| 连接池管理正确 | ✅ | ConnectionManager |
-| BRPOP 阻塞 | ✅ | 参数 `0` 阻塞等待 |
-
----
-
-## TCP 服务审查
-
-| 要求 | 状态 | 说明 |
-|------|------|------|
-| LF分隔JSON | ✅ | `LinesCodec` 按 `\n` 分割 |
-| 连接池内存HashMap | ✅ | `RwLock>` |
-| 登录验证流程 | ⚠️ | 仅注册连接,验证签名在 API 端完成 |
-| 断连清理 | ✅ | `tokio::select!` 退出时 `pool.remove` |
-
----
-
-## 配置文件审查
-
-| 要求 | 状态 | 说明 |
-|------|------|------|
-| 使用 config.toml | ❌ | 代码只读环境变量 |
-| 配置项完整 | ⚠️ | 缺少 `log_level` 等 |
-| 敏感信息不硬编码 | ❌ | config.toml 含数据库密码 |
-
----
-
-## 优点
-
-1. **服务职责分离干净** — device-server 确实只做 TCP 通信,handler.rs 轻量清晰
-2. **`tokio::select!` 读写分离** — `server.rs` 中同时处理设备消息读取和平台指令写入,设计合理
-3. **`FramedRead + LinesCodec`** — 正确处理了 LF 粘包问题
-4. **`reply_worker` 的 `oneshot` 机制** — 通过 msg_id 匹配异步等待,设计优雅
-5. **`report_worker` 每条消息 `tokio::spawn`** — 独立处理,不影响后续消息消费
-6. **全局 `#[allow(dead_code)]` 仅出现在少数必要位置**(device-server `connection.rs` 和 `protocol.rs` 的 `#![allow(dead_code)]` 是模块级,尚可接受)
-
----
-
-## 修复优先级总结
-
-| 优先级 | 问题 | 风险等级 |
-|--------|------|---------|
-| 🔴 P0 | register_node 与 heartbeat 的 Redis 类型冲突 | 严重 — 节点管理静默失效 |
-| 🟡 P1 | 节点管理双轨制 — 内存NodeRegistry与Redis注册无关联 | 高 — 节点列表API不可用 |
-| 🟡 P2 | 配置系统与要求背离(读env而非config.toml) | 中 — 视部署要求 |
-| 🟢 P3 | 协议结构体两服务重复定义 | 低 — 维护成本 |
-| 🟢 P4 | auth_str 限流器用同步Mutex | 低 — 规范性问题 |
-
-**推荐立即修复**:P0 + P1。这两个问题导致节点管理和心跳功能实际上不工作。
diff --git a/tasks/review-tcp-refactor.md b/tasks/review-tcp-refactor.md
deleted file mode 100644
index bd423b6..0000000
--- a/tasks/review-tcp-refactor.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# 代码审查任务:TCP服务重构
-
-## 目标
-
-审查TCP服务重构后的代码质量,确保符合架构设计。
-
-## 审查范围
-
-### API服务
-`software/api-server/src/` 目录下所有文件:
-- `main.rs` — 启动入口
-- `config.rs` — 配置管理
-- `routes/` — 路由模块
-- `workers/` — 后台协程
-- `middleware/` — 中间件
-
-### 设备服务
-`software/device-server/src/` 目录下所有文件:
-- `main.rs` — 启动入口
-- `config.rs` — 配置管理
-- `tcp/` — TCP服务模块
-- `redis.rs` — Redis连接
-
-## 审查清单
-
-### 1. 架构符合性
-- [ ] API服务和设备服务职责分离清晰
-- [ ] 设备服务不处理业务逻辑(不存DB、不验证签名)
-- [ ] 服务间通信用Redis LIST,不用HTTP直接调用
-- [ ] 节点注册/心跳/注销功能完整
-
-### 2. 异步处理
-- [ ] 所有指令异步处理,msg_id匹配响应
-- [ ] 后台协程处理Redis队列(report_worker/reply_worker)
-- [ ] 无阻塞操作(tokio::spawn合理使用)
-- [ ] 超时处理正确(60秒)
-
-### 3. Redis使用
-- [ ] 连接注册:`device:{imei}` → `{node_id, ip, port}`,TTL 180秒
-- [ ] 消息队列:`device:reports` / `device:commands` / `device:replies`
-- [ ] BRPOP阻塞弹出,不浪费CPU
-- [ ] 连接池管理正确
-
-### 4. TCP服务
-- [ ] LF分隔JSON消息
-- [ ] 连接池用内存HashMap(快速)
-- [ ] 设备登录验证流程正确
-- [ ] 断连清理逻辑
-
-### 5. 代码规范
-- [ ] `cargo check` 零报错
-- [ ] `cargo clippy` 零警告
-- [ ] 单函数≤80行
-- [ ] 命名语义化,完整注释
-- [ ] 错误处理完善,无裸panic
-
-### 6. 配置文件
-- [ ] 使用config.toml(不用.env)
-- [ ] 配置项完整(server/database/redis/jwt等)
-- [ ] 敏感信息不硬编码
-
-## 输出格式
-
-```markdown
-# TCP服务重构审查报告
-
-## 总体评价
-[评分1-10,简要评价]
-
-## 架构符合性
-[是否符合设计,问题列表]
-
-## 代码质量
-[编译/规范/错误处理]
-
-## 严重问题
-[必须修复的问题]
-
-## 警告
-[建议修复的问题]
-
-## 优点
-[值得肯定的设计]
-```
-
-## 质量约束
-
-1. 审查必须覆盖所有清单项目
-2. 每个问题必须给出具体位置和修复建议
-3. 审查报告用中文撰写
-4. 必须实际读取代码文件